def create_user_badge(cls, badge, from_user, to_user, post=None, comment=None, note=None): if from_user == to_user: raise BadRequest( title="🛑 Нельзя дарить награды самому себе", message="Это что такое-то вообще!" ) if badge.price_days >= from_user.membership_days_left(): raise InsufficientFunds( title="💸 Недостаточно средств :(", message=f"Вы не можете подарить юзеру эту награду, " f"так как у вас осталось {math.floor(from_user.membership_days_left())} дней членства, " f"а награда стоит {badge.price_days}. " f"Продлите членство в настройках своего профиля." ) with transaction.atomic(): # save user badge into the database try: user_badge = UserBadge.objects.create( badge=badge, from_user=from_user, to_user=to_user, post=post, comment=comment, note=note, ) except IntegrityError: raise ContentDuplicated( title="🛑 Вы уже дарили награду за этот пост или комментарий", message="Повторно награды дарить нельзя. Но вы можете подарить другую награду." ) # deduct days balance from profile User.objects\ .filter(id=from_user.id)\ .update( membership_expires_at=F("membership_expires_at") - timedelta(days=badge.price_days) ) # add badge to post/comment metadata (for caching purposes) comment_or_post = comment or post metadata = comment_or_post.metadata or {} badges = metadata.get("badges") or {} if badge.code not in badges: # add new badge badges[badge.code] = { "title": badge.title, "description": badge.description, "count": 1, } else: # if badge exists, increment badge count badges[badge.code]["count"] += 1 # update metadata only (do not use .save(), it saves all fields and can cause side-effects) metadata["badges"] = badges type(comment_or_post).objects.filter(id=comment_or_post.id).update(metadata=metadata) return user_badge
def create_or_edit(request, post_type, post=None, mode="create"): FormClass = POST_TYPE_MAP.get(post_type) or PostTextForm # show blank form on GET if request.method != "POST": form = FormClass(instance=post) return render(request, f"posts/compose/{post_type}.html", { "mode": mode, "post_type": post_type, "form": form, }) # validate form on POST form = FormClass(request.POST, request.FILES, instance=post) if form.is_valid(): if not request.me.is_moderator: if Post.check_duplicate(user=request.me, title=form.cleaned_data["title"], ignore_post_id=post.id if post else None): raise ContentDuplicated() is_ok = Post.check_rate_limits(request.me) if not is_ok: raise RateLimitException( title="🙅♂️ Слишком много постов", message= "В последнее время вы создали слишком много постов. Потерпите, пожалуйста." ) post = form.save(commit=False) if not post.author_id: post.author = request.me post.type = post_type post.html = None # flush cache post.save() if mode == "create" or not post.is_visible: PostSubscription.subscribe(request.me, post, type=PostSubscription.TYPE_ALL_COMMENTS) if post.is_visible: if post.topic: post.topic.update_last_activity() SearchIndex.update_post_index(post) LinkedPost.create_links_from_text(post, post.text) action = request.POST.get("action") if action == "publish": post.publish() LinkedPost.create_links_from_text(post, post.text) return redirect("show_post", post.type, post.slug) return render(request, f"posts/compose/{post_type}.html", { "mode": mode, "post_type": post_type, "form": form, })
def compose_type(request, post_type): if post_type not in dict(Post.TYPES): raise Http404() FormClass = POST_TYPE_MAP.get(post_type) or PostTextForm if request.method == "POST": form = FormClass(request.POST, request.FILES) if form.is_valid(): if not request.me.is_moderator: if Post.check_duplicate(user=request.me, title=form.cleaned_data["title"]): raise ContentDuplicated() is_ok = Post.check_rate_limits(request.me) if not is_ok: raise RateLimitException( title="🙅♂️ Слишком много постов", message= "В последнее время вы создали слишком много постов. Потерпите, пожалуйста." ) post = form.save(commit=False) post.author = request.me post.type = post_type post.save() PostSubscription.subscribe(request.me, post) if post.is_visible: if post.topic: post.topic.update_last_activity() SearchIndex.update_post_index(post) LinkedPost.create_links_from_text(post, post.text) if post.is_visible or request.POST.get("show_preview"): return redirect("show_post", post.type, post.slug) else: return redirect("compose") else: form = FormClass() return render(request, f"posts/compose/{post_type}.html", { "mode": "create", "form": form })