Reputation: 2025
I have an application where user create posts and can view them. the problem I face is with the edit button and how to make only users who created the post be able to edit their own post or delete them else another user can not. if I enter into the browser url field for example http://127.0.0.1:8000/soko/edit
it opens the form field and it is ready to be edited but if I use input button from my template I get the following error Reverse for 'edit' with no arguments not found. 1 pattern(s) tried: ['(?P<slug>[\\w-]+)/edit/$']
below is my views.py
def edit(request, slug = None):
instance = get_object_or_404(Post, slug = slug)
form = PostForm(request.POST or None, request.FILES or None, instance = instance)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
messages.success(request, "Post updated")
#redirecting to landing page
return HttpResponseRedirect(instance.get_absolute_url())
context = {
"title": instance.title,
"instance": instance,
"form": form,
}
template = 'edit.html'
return render(request, template, context)
def delete(request, slug=None):
instance = get_object_or_404(Post, slug=slug)
instance.delete()
messages.success(request, "Post Deleted")
return redirect(index)
urls.py
url(r'^(?P<slug>[\w-]+)/edit/$', views.edit, name='edit'),
url(r'^(?P<slug>[\w-]+)/delete/$', views.delete, name='delete'),
html
<a class="btn btn-default" href="{% url 'posts:edit' %}"></a>
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [
"title",
"content",
]
models.py
class Post(models.Model):
title = models.CharField(max_length = 120)
slug = models.SlugField(unique= True)
content = models.TextField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("posts:more", kwargs={"slug": self.slug})
Upvotes: 1
Views: 1246
Reputation: 985
Your Post model has no relation with the User model so to include the user instance in your form or views that relates to your Post model you need to add a relation like so:
# models.py
# include the User model
from django.contrib.auth.models import User
class Post(models.Model):
.....
created_by = models.ForeignKey(User)
And your views have to handle the requested user and it's relation with the instance plus validating and restricting the request access.
# views.py
# restrict the view to only the user who created the instance.
from django.core.exceptions import PermissionDenied
# A custom access decorator to check if the request.user
# is the owner of the instance
def restrict_user_access(function):
def wrap(request, *args, **kwargs):
posts = Post.objects.get(slug=kwargs['slug'])
if post.created_by != request.user:
raise PermissionDenied
return function(request, *args, **kwargs)
wrap.__doc__ = function.__doc__
wrap.__name__ = function.__name__
return wrap
@restrict_user_access
def edit(request, slug = None):
instance = get_object_or_404(Post, slug = slug)
form = PostForm(request.POST or None, request.FILES or None, instance =instance)
if form.is_valid():
....
# add the request.user to created_by
form.instance.created_by = request.user
form.save()
@restrict_user_access
def delete(request, slug=None):
.....
The reverse error happens because django can't resolve your url without the slug pattern as you described in your urls.py
so in your template you need to pass the post_context.slug in your url like so, change post_context to whatever context you are using in that view.
<a class="btn btn-default" href="{% url 'posts:edit' post_context.slug %}"></a>
edited: changed pk to slug.
Upvotes: 2