get() got an unexpected keyword argument 'pk': django - django

i'm creating like button for my django blog i import api to use ajex from django rest frameworks but i'm getting error in that the error is get() got an unexpected keyword argument 'pk' django
view.py
class PostLikeToggle(RedirectView):
def get_redirect_url(self, *args, **kwargs):
obj = get_object_or_404(Post, pk=kwargs['pk'])
url_ = obj.get_absolute_url()
user = self.request.user
if user.is_authenticated:
if user in obj.likes.all():
obj.likes.remove(user)
else:
obj.likes.add(user)
return url_
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
from django.contrib.auth.models import User
class PostLikeApiToggle(APIView):
authentication_classes = [authentication.SessionAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get(self, request, format=None):
usernames = [user.username for user in User.objects.all()]
return Response(usernames)
def get_redirect_url(self, *args, **kwargs):
obj = get_object_or_404(Post, pk=kwargs['pk'])
url_ = obj.get_absolute_url()
user = self.request.user
updated = False
liked =False
if user.is_authenticated:
if user in obj.likes.all():
liked = False
obj.likes.remove(user)
else:
liked = True
obj.likes.add(user)
updated = True
data = {
"updated":updated,
"liked":liked
}
return Response(data)
urls.py
path('blog/<int:pk>/like/', PostLikeToggle.as_view(),name='Like-Toggle'),
path('blog/api/<int:pk>/like/', PostLikeApiToggle.as_view(),name='Like-Api-Toggle'),
models.py
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User,on_delete=models.CASCADE)
likes =models.ManyToManyField(User,blank=True,related_name='post_likes')
content = models.TextField()
img = models.ImageField(upload_to='pics',blank=True)
time = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('LoveTravel-Details', kwargs={'pk': self.pk})
def get_like_url(self):
return reverse('Like-Toggle', kwargs={'pk':self.pk})
def get_api_like_url(self):
return reverse('Like-Api-Toggle', kwargs={'pk':self.pk})
can someone please help me get() got an unexpected keyword argument 'pk' in that error

The error is quite explicit. The get method is missing an argument.
In order to fix that:
class PostLikeApiToggle(APIView):
...
def get(self, request, pk, format=None):
Why did you get that error ?
Your urls.py defines the path as 'blog/<int:pk>/like/'. Django will resolve that url and call the view's method - get in your case - with the group names/values as keyword arguments - refer to the point 4 of the documentation for more details about how Django processes requests.

Related

How to check with decorator if user has access to some objects in Django

I'm working on a project, with structure as below. And I was trying to check if user is a member of the requested team and is allowed to see it's details. So I was trying to achieve this by decorators, but i'm still getting http 500 error.
I'm not sure if it's right solution and just bad implementation or it's better to make it in different way.
I'll be gratefull for Your help.
Team/Models.py
roles = (('admin', 'admin'), ('member', 'member'), ('guest', 'guest'))
class Team(models.Model):
name = models.CharField(max_length=150)
description = models.TextField(blank=True)
members = models.ManyToManyField(User, through='Membership')
cppTables = models.JSONField(default=list, blank=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'teams/{self.pk}/'
def has_user(self, user_id):
return self.members.get(pk=user_id).exists()
class Membership(models.Model):
member = models.ForeignKey(User, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
role = models.CharField(max_length=15, choices=roles, default='member')
def __str__(self):
return str(self.member)
class Meta:
unique_together = [['member', 'team']]
Team/Decorators.py
def required_membership(view_func):
def wrapper(request, *args, **kwargs):
team_id = kwargs['team_id']
team = Team.objects.get(pk=team_id)
if team.has_user(request.user.id):
return view_func(request, *args, **kwargs)
else:
raise HttpResponseForbidden
return wrapper
Team/Views.py
#method_decorator(required_membership, name="dispatch")
class TeamDetail(APIView):
def get_object(self, pk):
try:
return Team.objects.get(pk=int(pk))
except Team.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
team = self.get_object(pk)
serializer = TeamSerializer(team, many=False)
return Response(serializer.data)
in you decorator put wrapper inside inner function. your code will look something like this and plus no need to put it inside method decorator simple use like this #required_membership()
from django.core.exceptions import PermissionDenied
def required_membership():
def inner(view_func):
def wrapper(request, *args, **kwargs):
team_id = kwargs['team_id']
team = Team.objects.get(pk=team_id)
if team.has_user(request.user.id):
return view_func(request, *args, **kwargs)
else:
raise PermissionDenied
return wrapper
return inner
UPDATE
put decorator above get or post method instead of class
The simplest way is probably to just filter in the view:
from django.shortcuts import get_object_or_404
class TeamDetail(APIView):
def get_object(self, pk):
return get_object_or_404(Team, pk=pk, members=self.request.user)
def get(self, request, pk, format=None):
team = self.get_object(pk)
serializer = TeamSerializer(team, many=False)
return Response(serializer.data)
This will return a 404 in case the user is not a member of a team, which gives less information to exploit when the user aims to visit a team for which he is not a member.
You can also raise a HttpResponseForbidden instead by checking membership with:
from django.shortcuts import get_object_or_404
class TeamDetail(APIView):
def get_object(self, pk):
team = get_object_or_404(Team, pk=pk)
if not team.members.filter(pk=self.request.user.pk).exists():
raise HttpResponseForbidden
return team
def get(self, request, pk, format=None):
team = self.get_object(pk)
serializer = TeamSerializer(team, many=False)
return Response(serializer.data)
Your has_user(…) method has a bug: it should use .filter(…) since a model object has no .exists() method, and .get(…) will raise an error in case it can not find the item:
class Team(models.Model):
# …
def has_user(self, user_id):
return self.members.filter(pk=user_id).exists()

Django object on creation isn't stored in the database and the ID is returned as null

I'm creating a basic notes application where user can perform CRUD operation.
Following are my models, views and URLs.
from django.db import models
from accounts.models import User
from django.utils import timezone
class Notes(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.created_on = timezone.now()
self.last_updated = timezone.now()
def __str__(self):
return self.title
Views.py
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status
from rest_framework.response import Response
from . import serializers, models
class NotesViewSet(viewsets.ModelViewSet):
queryset = models.Notes.objects.all()
serializer_class = serializers.NotesSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def list(self, request):
notes = models.Notes.objects.all().filter(user=request.user)
serializer = serializers.NotesSerializer(notes, many=True)
return Response(serializer.data)
def create(self, request):
serializer = serializers.NotesSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
def retrieve(self, request, pk=None):
note = models.Notes.objects.filter(id=pk)
serializer = serializers.NotesSerializer(instance=note)
return Response(serializer.data)
def update(self, request, pk=None):
note = models.Notes.objects.get(id=pk)
serializer = serializers.NotesSerializer(
instance=note, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
def destroy(self, request, pk=None):
note = models.Notes.objects.get(id=pk)
note.delete()
return Response({"message": "Note deleted"},
status=status.HTTP_202_ACCEPTED)
urls.py
from django.urls import path
from .views import NotesViewSet
app_name = 'notes'
urlpatterns = [
path('get', NotesViewSet.as_view({
'get': 'list',
}), name='get'),
path('create', NotesViewSet.as_view({
'post': 'create',
}), name='create'),
path('get/<str:pk>', NotesViewSet.as_view({
'get': 'retrieve',
}), name='retrieve'),
path('update/<str:pk>', NotesViewSet.as_view({
'put': 'update',
}), name='update'),
path('delete/<str:pk>', NotesViewSet.as_view({
'delete': 'destroy',
}), name='delete'),
]
My question is, once I send a post request to create a new note something like this coming back as a response
{
"id": null,
"title": "title_1",
"body": "body_1",
"created_on": "2021-06-02T21:10:42.019236+05:30",
"last_updated": "2021-06-02T21:10:42.019257+05:30",
"user": 1
}
and the data isn't stored in the database as well. Please help
I believe the issue is in how you've overridden your save method- you still need to call the parent's save on it to get your expected behavior.
def save(self, *args, **kwargs):
if not self.id:
self.created_on = timezone.now()
self.last_updated = timezone.now()
super().save(*args, **kwargs)
Your code has some odd tabulation under your Notes class- I'm assuming this is just an issue in the code within the question.
super()
in save method is missing. You need to call super().save(*args, **kwargs) to make it work.

Django FormView: Add if record does not exist and update if it does exist

I created a FormView and it works fine if the user executed the process the first time. However when it is executed the second time I get an error that the record already exist. This is expected as the user in the model is unique. How can I overcome this problem so that the current record is overwritten by the form.save if the record already exist.
models.py
class ttemp_selection(models.Model):
select_account = models.ForeignKey(tledger_account, on_delete=models.CASCADE)
date_from = models.DateField(default=datetime.today)
date_to = models.DateField(default=datetime.today)
user = models.ForeignKey(custom_user, on_delete=models.CASCADE, unique=True)
def __str__(self):
return self.select_account
forms.py
class Meta:
model = ttemp_selection
fields = ['select_account', 'date_from', 'date_to', 'user']
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super(SelectAccountForm, self).__init__(*args, **kwargs)
user = self.request.user
current_company = user.current_company
self.fields['select_account'].queryset = tledger_account.objects.filter(
company=current_company, gl_category='Cash and Bank')
view.py
class sasView(FormView):
template_name = 'cashflow/select_account.html'
form_class = SelectAccountForm
success_url = 'home'
def form_valid(self, form):
form.save()
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super(sasView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
I can determine the record by using ttemp_selection.objects.get(user=request.user)
I know I can make use of the UpdateView class but that will create a problem when the record does not exist. It will also add an extra step that is unnecessary.
Assistance will be appreciated.
You can work with a CreateView, and slightly alter the behavior to specify a self.object if that exists:
from django.contrib.auth.mixins import LoginRequiredMixin
class sasView(LoginRequiredMixin, CreateView):
template_name = 'cashflow/select_account.html'
form_class = SelectAccountForm
success_url = 'home'
def get_form(self, *args, **kwargs):
self.object = ttemp_selection.objects.filter(
user=self.request.user
).first()
return super().get_form(*args, **kwargs)
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super(sasView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
It however makes no sense to include the user as field, since - if I understand it correctly - you use the logged in user. By including it, you make it possible that a person forges a POST request, and thus changes the account of a different user. You should omit this filed:
class SelectAccountForm(forms.ModelForm):
class Meta:
model = ttemp_selection
# no user &downarrow;
fields = ['select_account', 'date_from', 'date_to']
# …
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].

django authorization in class-based view

I'm trying to restrict access of CRUD pages to the owners, but I can't find the class-based view equivalent of "if request.user != post.author raise Http404". Thx for your time.
models.py
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article_detail', args=[str(self.id)])
views.py
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ['title', 'body']
template_name = 'article_edit.html'
login_url = 'login'
I tried the following (and many other combination arround those lines), but it isn't working.
def get(self, request, *args, **kwargs):
if self.request.user == self.obj.author:
raise Http404()
Youn can do something like this:-
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ['title', 'body']
template_name = 'article_edit.html'
login_url = 'login'
def get(self, request, *args, **kwargs):
self.obj = self.get_object()
if self.request.user != self.obj.author:
raise Http404()
return super(ArticleUpdateView, self).get(request, *args, **kwargs)
I think you can override the get_queryset method to achieve this. For example:
class ArticleUpdateView(...):
def get_queryset(self):
queryset = super(ArticleUpdateView, self).get_queryset()
return queryset.filter(author = self.request.user)
So, when a user tries to update an post which is not created by him, then he will not be able to get it because will not be able find the post object in Queryset provided by get_queryset method. For details, please SingleObjectMixin which is later sub-classed by UpdateView. FYI you don't need to override the get method for this implementation.

How to solve IndexError in Django 1.8.6

I am getting list index out of range error in my application. It is an e-learning system and the error occurs when a student tries to enroll in for a course. I am using django 1.8.6. This is my error.
IndexError at /students/course/12/
list index out of range
Request Method: GET
Request URL: http://127.0.0.1:8000/students/course/12/
Django Version: 1.8.6
Exception Type: IndexError
Exception Value: list index out of range
Exception Location: D:\DjangoSchoolProject\env\educa\lib\site-
packages\django\db\models\query.py in __getitem__, line 201
Python Executable: D:\DjangoSchoolProject\env\educa\Scripts\python.exe
Python Version: 3.6.5
Python Path:
['D:\\DjangoSchoolProject\\educa',
'D:\\DjangoSchoolProject\\env\\educa\\Scripts\\python36.zip',
'D:\\DjangoSchoolProject\\env\\educa\\DLLs',
'D:\\DjangoSchoolProject\\env\\educa\\lib',
'D:\\DjangoSchoolProject\\env\\educa\\Scripts',
'c:\\users\\greendelta\\appdata\\local\\programs\\python\\python36-
32\\Lib',
'c:\\users\\greendelta\\appdata\\local\\programs\\python\\python36-
32\\DLLs',
'D:\\DjangoSchoolProject\\env\\educa',
'D:\\DjangoSchoolProject\\env\\educa\\lib\\site-packages']
and this is the python file I am using to enroll student.
from django import forms
from courses.models import Course
class CourseEnrollForm(forms.Form):
course = forms.ModelChoiceField(queryset=Course.objects.all(),
widget=forms.HiddenInput)
Any help will be greatly appreciated. I have also included my view.py file below
Here is my view.py file.
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import get_object_or_404, redirect
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.list import ListView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.forms.models import modelform_factory
from django.db.models import Count
from django.apps import apps
from django.core.cache import cache
from braces.views import LoginRequiredMixin, PermissionRequiredMixin, \
CsrfExemptMixin, JsonRequestResponseMixin
from students.forms import CourseEnrollForm
from .models import Subject, Course, Module, Content
from .forms import ModuleFormSet
class OwnerMixin(object):
def get_queryset(self):
qs = super(OwnerMixin, self).get_queryset()
return qs.filter(owner=self.request.user)
class OwnerEditMixin(object):
def form_valid(self, form):
form.instance.owner = self.request.user
return super(OwnerEditMixin, self).form_valid(form)
class OwnerCourseMixin(OwnerMixin, LoginRequiredMixin):
model = Course
class OwnerCourseEditMixin(OwnerCourseMixin, OwnerEditMixin):
fields = ['subject', 'title', 'slug', 'overview']
success_url = reverse_lazy('manage_course_list')
template_name = 'courses/manage/course/form.html'
class ManageCourseListView(OwnerCourseMixin, ListView):
template_name = 'courses/manage/course/list.html'
class CourseCreateView(PermissionRequiredMixin,
OwnerCourseEditMixin,
CreateView):
permission_required = 'courses.add_course'
class CourseUpdateView(PermissionRequiredMixin,
OwnerCourseEditMixin,
UpdateView):
permission_required = 'courses.change_course'
class CourseDeleteView(PermissionRequiredMixin,
OwnerCourseMixin,
DeleteView):
success_url = reverse_lazy('manage_course_list')
template_name = 'courses/manage/course/delete.html'
permission_required = 'courses.delete_course'
class CourseModuleUpdateView(TemplateResponseMixin, View):
template_name = 'courses/manage/module/formset.html'
course = None
def get_formset(self, data=None):
return ModuleFormSet(instance=self.course,
data=data)
def dispatch(self, request, pk):
self.course = get_object_or_404(Course, id=pk, owner=request.user)
return super(CourseModuleUpdateView, self).dispatch(request, pk)
def get(self, request, *args, **kwargs):
formset = self.get_formset()
return self.render_to_response({'course': self.course,
'formset': formset})
def post(self, request, *args, **kwargs):
formset = self.get_formset(data=request.POST)
if formset.is_valid():
formset.save()
return redirect('manage_course_list')
return self.render_to_response({'course': self.course,
'formset': formset})
class ContentCreateUpdateView(TemplateResponseMixin, View):
module = None
model = None
obj = None
template_name = 'courses/manage/content/form.html'
def get_model(self, model_name):
if model_name in ['text', 'video', 'image', 'file']:
return apps.get_model(app_label='courses', model_name=model_name)
return None
def get_form(self, model, *args, **kwargs):
Form = modelform_factory(model,
exclude=['owner', 'order', 'created', 'updated'])
return Form(*args, **kwargs)
def dispatch(self, request, module_id, model_name, id=None):
self.module = get_object_or_404(Module,
id=module_id,
course__owner=request.user)
self.model = self.get_model(model_name)
if id:
self.obj = get_object_or_404(self.model,
id=id,
owner=request.user)
return super(ContentCreateUpdateView,
self).dispatch(request, module_id, model_name, id)
def get(self, request, module_id, model_name, id=None):
form = self.get_form(self.model, instance=self.obj)
return self.render_to_response({'form': form,
'object': self.obj})
def post(self, request, module_id, model_name, id=None):
form = self.get_form(self.model,
instance=self.obj,
data=request.POST,
files=request.FILES)
if form.is_valid():
obj = form.save(commit=False)
obj.owner = request.user
obj.save()
if not id:
# new content
Content.objects.create(module=self.module,
item=obj)
return redirect('module_content_list', self.module.id)
return self.render_to_response({'form': form,
'object': self.obj})
class ContentDeleteView(View):
def post(self, request, id):
content = get_object_or_404(Content,
id=id,
module__course__owner=request.user)
module = content.module
content.item.delete()
content.delete()
return redirect('module_content_list', module.id)
class ModuleContentListView(TemplateResponseMixin, View):
template_name = 'courses/manage/module/content_list.html'
def get(self, request, module_id):
module = get_object_or_404(Module,
id=module_id,
course__owner=request.user)
return self.render_to_response({'module': module})
class ModuleOrderView(CsrfExemptMixin, JsonRequestResponseMixin, View):
def post(self, request):
for id, order in self.request_json.items():
Module.objects.filter(id=id,
course__owner=request.user).update(order=order)
return self.render_json_response({'saved': 'OK'})
class ContentOrderView(CsrfExemptMixin, JsonRequestResponseMixin, View):
def post(self, request):
for id, order in self.request_json.items():
Content.objects.filter(id=id,
module__course__owner=request.user).update(order=order)
return self.render_json_response({'saved': 'OK'})
class CourseListView(TemplateResponseMixin, View):
model = Course
template_name = 'courses/course/list.html'
def get(self, request, subject=None):
subjects = cache.get('all_subjects')
if not subjects:
subjects = Subject.objects.annotate(total_courses=Count('courses'))
cache.set('all_subjects', subjects)
all_courses = Course.objects.annotate(total_modules=Count('modules'))
if subject:
subject = get_object_or_404(Subject, slug=subject)
key = 'subject_{}_courses'.format(subject.id)
courses = cache.get(key)
if not courses:
courses = all_courses.filter(subject=subject)
cache.set(key, courses)
else:
courses = cache.get('all_courses')
if not courses:
courses = all_courses
cache.set('all_courses', courses)
return self.render_to_response({'subjects': subjects,
'subject': subject,
'courses': courses})
class CourseDetailView(DetailView):
model = Course
template_name = 'courses/course/detail.html'
def get_context_data(self, **kwargs):
context = super(CourseDetailView, self).get_context_data(**kwargs)
context['enroll_form'] = CourseEnrollForm(initial={'course':self.object})
return context
I still do not have a working solution to my problem.
Kinda hard give any good answer without the entire code, please post your view.py thats render your page
This usualy happends when your try to access array with fixed position without checking if this exists or have this currently position
someArray = [1,2,3]
print(someArray[200])
Probabily you trying to acess your queryset or any array with wrong position