удалить лишнего из новостей
This commit is contained in:
parent
7e410ebff8
commit
5dce32c303
@ -1,25 +0,0 @@
|
||||
from django import forms
|
||||
from .models import Post, Comment
|
||||
|
||||
|
||||
class PostForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Post
|
||||
fields = ['type', 'department', 'title', 'body', 'image', 'attachment']
|
||||
widgets = {
|
||||
'type': forms.Select(attrs={'class': 'form-select'}),
|
||||
'department': forms.Select(attrs={'class': 'form-select'}),
|
||||
'title': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'body': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
|
||||
'image': forms.ClearableFileInput(attrs={'class': 'form-control'}),
|
||||
'attachment': forms.ClearableFileInput(attrs={'class': 'form-control'}),
|
||||
}
|
||||
|
||||
|
||||
class CommentForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ['text']
|
||||
widgets = {
|
||||
'text': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
from django import forms
|
||||
|
||||
TYPE_COMPANY = "company"
|
||||
TYPE_DEPARTMENT = "department"
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
email = forms.EmailField(label="Email")
|
||||
password = forms.CharField(label="Пароль", widget=forms.PasswordInput)
|
||||
|
||||
|
||||
class PostCreateForm(forms.Form):
|
||||
type = forms.ChoiceField(
|
||||
choices=[(TYPE_COMPANY, "Главная (компания)"), (TYPE_DEPARTMENT, "Отдел")]
|
||||
)
|
||||
department = forms.IntegerField(
|
||||
required=False, min_value=1, label="Отдел (для новостей отдела)"
|
||||
)
|
||||
title = forms.CharField(max_length=200, label="Заголовок")
|
||||
body = forms.CharField(widget=forms.Textarea, label="Текст")
|
||||
image = forms.ImageField(required=False, label="Изображение")
|
||||
attachment = forms.FileField(required=False, label="Вложение")
|
||||
|
||||
|
||||
class CommentForm(forms.Form):
|
||||
post = forms.IntegerField(widget=forms.HiddenInput)
|
||||
text = forms.CharField(
|
||||
required=False, widget=forms.Textarea(attrs={"rows": 2}), label="Комментарий"
|
||||
)
|
||||
image = forms.ImageField(required=False, label="Изображение")
|
||||
attachment = forms.FileField(required=False, label="Файл")
|
||||
|
||||
def clean(self):
|
||||
cleaned = super().clean()
|
||||
has_any = bool(
|
||||
(cleaned.get("text") or "").strip()
|
||||
or cleaned.get("image")
|
||||
or cleaned.get("attachment")
|
||||
)
|
||||
if not has_any:
|
||||
raise forms.ValidationError("Нужно указать текст, изображение или файл.")
|
||||
return cleaned
|
||||
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@ -1,14 +0,0 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views_front
|
||||
|
||||
app_name = "feed"
|
||||
|
||||
urlpatterns = [
|
||||
path("", views_front.feed_list, name="feed_list"),
|
||||
path("departments/<int:pk>/", views_front.department_feed, name="department_feed"),
|
||||
path("employees/<int:pk>/", views_front.employee_feed, name="employee_feed"),
|
||||
path("post/<int:pk>/", views_front.post_detail, name="post_detail"),
|
||||
path("post/<int:pk>/pin/", views_front.pin_post, name="pin_post"),
|
||||
path("post/<int:pk>/like/", views_front.toggle_like, name="toggle_like"),
|
||||
]
|
||||
@ -1,252 +0,0 @@
|
||||
# backend/feed/views.py
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseRedirect, JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from api.client import ApiResponse, get_api_client
|
||||
from .constants import TYPE_COMPANY, TYPE_DEPARTMENT
|
||||
from .forms import CommentForm
|
||||
|
||||
# ---------- Константы путей API ----------
|
||||
API_POSTS = "v1/posts/"
|
||||
API_COMMENTS = "v1/comments/"
|
||||
API_DEPARTMENTS = "v1/departments/"
|
||||
API_EMPLOYEES = "v1/employees/"
|
||||
|
||||
|
||||
# ---------- Вспомогательное: доступ к ApiClient ----------
|
||||
def _api(request):
|
||||
return get_api_client(request)
|
||||
|
||||
|
||||
def _paged_or_list(resp: ApiResponse):
|
||||
"""Извлекает results из пагинированного ответа или возвращает сырой список."""
|
||||
data = resp.json or {}
|
||||
return data.get("results", data)
|
||||
|
||||
|
||||
def _extract_page_data(resp: ApiResponse):
|
||||
"""Извлекает данные пагинации из ответа API.
|
||||
|
||||
Returns:
|
||||
tuple: (items, count, next_url, prev_url)
|
||||
"""
|
||||
data = resp.json or {}
|
||||
if isinstance(data, dict) and "results" in data:
|
||||
return (
|
||||
data["results"],
|
||||
data.get("count", 0),
|
||||
data.get("next"),
|
||||
data.get("previous"),
|
||||
)
|
||||
return data if isinstance(data, list) else [], 0, None, None
|
||||
|
||||
|
||||
# ---------- Хелперы API для ленты ----------
|
||||
def _api_list_posts(request, **params) -> list[dict]:
|
||||
resp = _api(request).get(API_POSTS, params=params)
|
||||
if not resp.ok:
|
||||
messages.error(request, f"Не удалось получить публикации: {resp.status}")
|
||||
return []
|
||||
return _paged_or_list(resp)
|
||||
|
||||
|
||||
def _api_get_post(request, post_id: int) -> dict | None:
|
||||
resp = _api(request).get(f"{API_POSTS}{post_id}/")
|
||||
if not resp.ok:
|
||||
messages.error(request, f"Публикация недоступна: {resp.status}")
|
||||
return None
|
||||
return resp.json or None
|
||||
|
||||
|
||||
def _api_get_comments_for_post(request, post_id: int) -> list[dict]:
|
||||
resp = _api(request).get(
|
||||
API_COMMENTS, params={"post": post_id, "ordering": "created_at"}
|
||||
)
|
||||
if not resp.ok:
|
||||
messages.error(request, f"Не удалось получить комментарии: {resp.status}")
|
||||
return []
|
||||
return _paged_or_list(resp)
|
||||
|
||||
|
||||
def _api_get_department(request, dept_id: int) -> dict | None:
|
||||
resp = _api(request).get(f"{API_DEPARTMENTS}{dept_id}/")
|
||||
return resp.json if resp.ok else None
|
||||
|
||||
|
||||
def _api_get_employee(request, employee_id: int) -> dict | None:
|
||||
resp = _api(request).get(f"{API_EMPLOYEES}{employee_id}/")
|
||||
return resp.json if resp.ok else None
|
||||
|
||||
|
||||
def _api_list_department_members(request, dept_id: int) -> list[dict]:
|
||||
# Предпочтительно: /v1/employees/?department=<id>&is_active=true&ordering=last_name,first_name
|
||||
resp = _api(request).get(
|
||||
API_EMPLOYEES,
|
||||
params={
|
||||
"department": dept_id,
|
||||
"is_active": "true",
|
||||
"ordering": "last_name,first_name",
|
||||
},
|
||||
)
|
||||
return _paged_or_list(resp) if resp.ok else []
|
||||
|
||||
|
||||
def _api_list_new_employees(request, limit: int = 10) -> list[dict]:
|
||||
"""Получить новых сотрудников через API"""
|
||||
since = (timezone.now() - timedelta(days=14)).isoformat()
|
||||
resp = _api(request).get(
|
||||
API_EMPLOYEES,
|
||||
params={
|
||||
"active": "true",
|
||||
"created_at__gte": since,
|
||||
"ordering": "-created_at",
|
||||
"page_size": limit,
|
||||
},
|
||||
)
|
||||
return _paged_or_list(resp) if resp.ok else []
|
||||
|
||||
|
||||
# ---------- Навигационные хелперы ----------
|
||||
def _post_back_url(post: dict, user) -> str:
|
||||
t = post.get("type")
|
||||
if t == TYPE_COMPANY:
|
||||
return reverse("feed:feed_list")
|
||||
if t == TYPE_DEPARTMENT and post.get("department_id"):
|
||||
return reverse("employees:department_detail", args=[post["department_id"]])
|
||||
# страница пользователя — все его публикации
|
||||
author_id = post.get("author_id") or (
|
||||
user.pk if getattr(user, "is_authenticated", False) else None
|
||||
)
|
||||
return reverse("feed:employee_feed", args=[author_id or 0])
|
||||
|
||||
|
||||
# ---------- Ленты ----------
|
||||
|
||||
|
||||
def feed_list(request):
|
||||
"""
|
||||
Лента компании - посты и новые сотрудники загружаются через API на клиенте.
|
||||
"""
|
||||
return render(
|
||||
request,
|
||||
"feed/feed_list.html",
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def department_feed(request, pk):
|
||||
"""Лента отдела"""
|
||||
department = _api_get_department(request, pk)
|
||||
if not department:
|
||||
messages.error(request, "Отдел недоступен.")
|
||||
return redirect("feed:feed_list")
|
||||
posts = _api_list_posts(request, type=TYPE_DEPARTMENT, department=pk)
|
||||
emp_links = _api_list_department_members(request, pk)
|
||||
return render(
|
||||
request,
|
||||
"feed/feed_list.html",
|
||||
{
|
||||
"department": department,
|
||||
"posts": posts,
|
||||
"new_employees": [],
|
||||
"emp_links": emp_links,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def employee_feed(request, pk):
|
||||
"""Лента сотрудника"""
|
||||
employee = _api_get_employee(request, pk)
|
||||
if not employee:
|
||||
messages.error(request, "Сотрудник недоступен.")
|
||||
return redirect("feed:feed_list")
|
||||
posts = _api_list_posts(request, author=pk)
|
||||
return render(
|
||||
request, "feed/feed_list.html", {"employee": employee, "posts": posts}
|
||||
)
|
||||
|
||||
|
||||
# ---------- Детальная + комментарии ----------
|
||||
|
||||
|
||||
@login_required
|
||||
def post_detail(request, pk):
|
||||
"""Рендерит детальную страницу поста с комментариями. CRUD через API в JavaScript."""
|
||||
post = _api_get_post(request, pk)
|
||||
if not post:
|
||||
return redirect("feed:feed_list")
|
||||
|
||||
comments = _api_get_comments_for_post(request, pk)
|
||||
form = CommentForm(initial={"post": pk})
|
||||
|
||||
return render(
|
||||
request,
|
||||
"feed/post_detail.html",
|
||||
{"post": post, "comments": comments, "comment_form": form},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def pin_post(request, pk):
|
||||
post = _api_get_post(request, pk)
|
||||
if not post:
|
||||
return redirect("feed:feed_list")
|
||||
action = "unpin" if post.get("pinned") else "pin"
|
||||
resp = _api(request).post(f"{API_POSTS}{pk}/{action}/")
|
||||
if resp.ok:
|
||||
messages.success(
|
||||
request, "Новость закреплена!" if action == "pin" else "Новость откреплена!"
|
||||
)
|
||||
else:
|
||||
messages.error(request, f"Операция недоступна: {resp.status}")
|
||||
return redirect(_post_back_url(post, request.user))
|
||||
|
||||
|
||||
# ---------- Лайки ----------
|
||||
|
||||
|
||||
@login_required
|
||||
def toggle_like(request, pk):
|
||||
if request.method != "POST":
|
||||
return redirect("feed:post_detail", pk=pk)
|
||||
|
||||
op = (request.POST.get("op") or "like").lower()
|
||||
if op not in {"like", "unlike"}:
|
||||
op = "like"
|
||||
|
||||
endpoint = "like" if op == "like" else "unlike"
|
||||
resp = _api(request).post(f"{API_POSTS}{pk}/{endpoint}/")
|
||||
|
||||
if not resp.ok:
|
||||
messages.error(request, f"Не удалось {op} публикацию: {resp.status}")
|
||||
|
||||
wants_json = (
|
||||
"application/json" in request.headers.get("Accept", "")
|
||||
or request.headers.get("X-Requested-With") == "XMLHttpRequest"
|
||||
)
|
||||
if wants_json:
|
||||
data = resp.json or {}
|
||||
return JsonResponse(
|
||||
{
|
||||
"ok": bool(resp.ok),
|
||||
"post_id": pk,
|
||||
"liked": data.get("liked"),
|
||||
"likes": data.get("likes_count"),
|
||||
}
|
||||
)
|
||||
|
||||
next_url = (
|
||||
request.POST.get("next")
|
||||
or request.META.get("HTTP_REFERER")
|
||||
or reverse("feed:post_detail", args=[pk])
|
||||
)
|
||||
return HttpResponseRedirect(next_url)
|
||||
Loading…
x
Reference in New Issue
Block a user