Search code examples
pythondjangodjango-modelsdjango-viewsmany-to-many

I have a table in Django with ManyToManyField. Can't figure out how to update a table entry


I have a table with posts that can have multiple categories, and a table with categories that can have multiple posts. models.py:

class Category(models.Model):
   name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=25)
    body = models.TextField()
    image = models.ImageField(blank=True)
    created_on = models.DateTimeField(auto_now_add=True)
    last_modified = models.DateTimeField(auto_now=True)
    categories = models.ManyToManyField('Category', related_name='posts', blank=True)
    profile = models.ForeignKey('Profile', verbose_name='User',
                                on_delete=models.CASCADE,
                                related_name='profile')

    def __str__(self):
        return self.title

views.py

Сlass ListCategoryView(generic.ListView):

    def get(self, request, *args, **kwargs):
        category = kwargs['category']
        posts = Post.objects.filter(categories__name__contains=category).order_by('-created_on')
        context = {
            "category": category,
            "posts": posts
        }
        return render(request, "list_category.html", context)

class ListPostView(generic.ListView):

    model = Post
    context_object_name = 'posts'
    template_name = 'list_post.html'

    def get_queryset(self):
        queryset = super().get_queryset()
        queryset = queryset.order_by('-created_on')
        return queryset

class CreatePostView(LoginRequiredMixin, generic.CreateView):

    model = Post
    template_name = 'create_post.html'
    form_class = PostDocumentForm

    def post(self, request, *args, **kwargs):
        blog_form = PostDocumentForm(request.POST, request.FILES)
        if blog_form.is_valid():
            categories = Category.objects.create(name=blog_form.cleaned_data['categories'])
            title = blog_form.cleaned_data.get('title')
            body = blog_form.cleaned_data.get('body')
            profile = request.user.profile
            image = self.get_image(blog_form)
            instance = Post.objects.create(title=title, body=body, profile=profile, image=image)
            instance.categories.set([categories])
            return HttpResponseRedirect('/blog/')
        return render(request, 'create_post.html', context={'form': blog_form})

    def get_image(self, form):
        image = form.cleaned_data.get('image')
        return image


class EditPostView(generic.UpdateView):

    form_class = PostDocumentForm
    model = Post
    template_name = 'edit_post.html'
    success_url = '/blog/'

forms.py:

class CategoryDocumentForm(forms.ModelForm):
    class Meta:
        model = Category
        fields = ('name',)


class PostDocumentForm(forms.ModelForm):
    categories = forms.CharField(min_length=3, max_length=100, required=False)

    class Meta:
        model = Post
        fields = ('title', 'body', 'image', 'categories')

I can't figure out how to update the post so that the categories are updated as well. I looked for many solutions, but none of them helped. I tried get_or_create, update, delete, then create again, but nothing worked. Better, as in most social networks - add a tag(here a category) manually, without selecting from the list of possible ones


Solution

  • This might help you:

    class PostEditView(UpdateView):
         def form_valid(self, form):
            # Take care of creating of updating your post with cleaned data
            # by yourself
    
            category_tokens = form.cleaned_data['categories'].split()
            categories = set()
            for token in category_tokens:
                try:
                    category = Category.objects.get(name=token)
                except ObjectDoesNotExist:
                    category = Category.objects.create(name=token)
                
                categories.add(category)
    
            # now you need to add the categories which are new to this post
            # and delete the categories which do not belong anymore to your post
            current_posts_categories = set(post_instance.categories_set.all())
            categories_to_add = categories - current_posts_categories
            categories_to_delete = current_posts_categories - categories
    
            # further handling is up to you ...