Search code examples
pythondjangodjango-taggit

Django-Taggit saving tags from a model field


I am building a post editor for my website. One of the fields I can edit is for tags. However when I save the post everything updates correctly and no errors are thrown but any changes I made to the tags field are not saved.

# Views.py
class EditPostView(UpdateView):
    form_class = EditPostForm
    model = Post
    template_name = 'myapp/editpost.html'

    def get(self, request, pk):

        if (not request.user.is_superuser):
            return HttpResponseForbidden()

        post = Post.objects.get(id=pk)
        if (post is None):
            return HttpResponseNotFound()

        form = self.form_class(instance=post)

        return render(request, self.template_name, {'form': form, 'post': post})

    def post(self, request, pk):

        if (not request.user.is_superuser):
            return HttpResponseForbidden()

        post = Post.objects.get(id=pk)
        if (post is None):
            return HttpResponseNotFound()

        form = self.form_class(request.POST, instance=post)

        if (form.is_valid()):
            post.title = form.cleaned_data['title']
            post.content_type = form.cleaned_data['content_type']
            post.screenshot = form.cleaned_data['screenshot']
            post.tags = form.cleaned_data['tags']
            post.body = form.cleaned_data['body']
            post.nsfw = form.cleaned_data['nsfw']
            post.allow_comments = form.cleaned_data['allow_comments']
            post.display_edited = form.cleaned_data['display_edited']
            post.files = form.cleaned_data['files']

            post.date_edited = datetime.now()

            post.save()

            return redirect('/posts/' + str(post.id))
        else:
            return HttpResponseNotFound()

        return HttpResponseNotFound()

#Forms.py
class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content_type', 'screenshot', 'body', 'tags', 'nsfw', 'allow_comments', 'files']
        widgets = {
            'screenshot': forms.TextInput(attrs={'placeholder': 'URL...'}),
            'tags': TagWidget(),
        }
        labels = {
            'files': 'Attachments',
        }

# Models.py
class Post(models.Model):
    title = models.CharField(max_length=256)
    disclaimer = models.CharField(max_length=256, blank=True)
    BLOGS = 'blogs'
    APPLICATIONS = 'applications'
    GAMES = 'games'
    WEBSITES = 'websites'
    GALLERY = 'gallery'
    PRIMARY_CHOICES = (
        (BLOGS, 'Blogs'),
        (APPLICATIONS, 'Applications'),
        (GAMES, 'Games'),
        (WEBSITES, 'Websites'),
    )
    content_type = models.CharField(max_length=256, choices=PRIMARY_CHOICES, default=BLOGS)
    screenshot = models.CharField(max_length=256, blank=True)
    tags = TaggableManager()
    body = RichTextField()
    date_posted = models.DateTimeField(default=datetime.now)
    date_edited = models.DateTimeField(blank=True, null=True)
    visible = models.BooleanField(default=True)
    nsfw = models.BooleanField()
    display_edited = models.BooleanField(default=False)
    allow_comments = models.BooleanField(default=True)
    files = models.ManyToManyField(File, blank=True)

    def __str__(self):
        if (self.visible == False):
            return '(Hidden) ' + self.title + ' in ' + self.content_type
        return self.title + ' in ' + self.content_type

Solution

  • You're doing a lot of unnecessary work here. You're not leveraging the power of the UpdateView. This is all you should need. It will call form.save() and everything for you

    from django.http import HttpResponseForbidden
    from django.shortcuts import get_object_or_404
    from django.views.generic.edit import UpdateView
    
    class EditPostView(UpdateView):
        form_class = EditPostForm
        model = Post
        template_name = 'myapp/editpost.html'
    
        def dispatch(self, *args, **kwargs):
            if not self.request.user.is_superuser:
                return HttpResponseForbidden()
            return super(EditPostView, self).dispatch(*args, **kwargs)
    
        def get_object(self, queryset=None):
            return get_object_or_404(Post, id=self.kwargs['id'])
    
        def get_success_url(self):
            return '/posts/{0}'.format(self.object.id)
    

    Edit: bonus points if you get rid of get_success_url and add a get_absolute_url method to the Post model.

    class Post(models.Model):
        ...
    
        def save(self, *args, **kwargs):
            self.date_edited = datetime.now()
            super(Post, self).save(*args, **kwargs)
    
        def get_absolute_url(self):
            return '/posts/{0}'.format(self.id)