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
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 ...