King
King

Reputation: 2025

editing a slug post in django

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

Answers (1)

misraX
misraX

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

Related Questions