Skip to content

Commit a50191b

Browse files
committed
Merge branch 'master' of https://github.com/PROCOLLAB-github/api
2 parents 563529b + a584d57 commit a50191b

25 files changed

Lines changed: 549 additions & 179 deletions

news/admin.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,59 @@
11
from django.contrib import admin
22

3-
from news.models import News, NewsTag
3+
from news.models import News
44

55

66
@admin.register(News)
77
class NewsAdmin(admin.ModelAdmin):
8-
list_display = ("id", "title", "tags_str")
9-
list_display_links = (
8+
# todo
9+
list_display = (
1010
"id",
11-
"title",
11+
"content_type",
12+
"object_id",
13+
"text",
14+
"datetime_created",
15+
"datetime_updated",
1216
)
13-
14-
15-
@admin.register(NewsTag)
16-
class NewsTagAdmin(admin.ModelAdmin):
17-
list_display = ("name", "description", "datetime_created", "datetime_updated", "id")
1817
list_display_links = (
1918
"id",
20-
"name",
19+
"content_type",
20+
"object_id",
21+
"text",
22+
"datetime_created",
23+
"datetime_updated",
24+
)
25+
list_filter = (
26+
"datetime_created",
27+
"datetime_updated",
28+
)
29+
search_fields = ("text",)
30+
readonly_fields = (
31+
"datetime_created",
32+
"datetime_updated",
2133
)
34+
# fieldsets = (
35+
# (
36+
# None,
37+
# {
38+
# "fields": (
39+
# "content_type",
40+
# "object_id",
41+
# "text",
42+
# "files",
43+
# )
44+
# },
45+
# ),
46+
# (
47+
# "Даты",
48+
# {
49+
# "fields": (
50+
# "datetime_created",
51+
# "datetime_updated",
52+
# )
53+
# },
54+
# ),
55+
# )
56+
# filter_horizontal = (
57+
# "files",
58+
# )
59+
# save_on_top = True

news/filters.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ class NewsFilter(filters.FilterSet):
1818
.filter(datetime_created__gt=datetime.datetime(...))
1919
"""
2020

21-
title__contains = filters.Filter(field_name="title", lookup_expr="contains")
21+
# title__contains = filters.Filter(field_name="title", lookup_expr="contains")
2222
text__contains = filters.Filter(field_name="text", lookup_expr="contains")
2323
datetime_created__gt = filters.DateTimeFilter(
2424
field_name="datetime_created", lookup_expr="gt"
2525
)
2626

2727
class Meta:
2828
model = News
29-
fields = ("title__contains", "text__contains", "datetime_created__gt")
29+
fields = (
30+
# "title__contains",
31+
"text__contains",
32+
"datetime_created__gt",
33+
)

news/managers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
class NewsManager(models.Manager):
66
def get_news(self, obj):
77
obj_type = ContentType.objects.get_for_model(obj)
8-
return self.get_queryset().filter(content_type=obj_type, object_id=obj.id)
8+
return self.get_queryset().filter(content_type=obj_type, object_id=obj.pk)
99

1010
def add_news(self, obj, **kwargs):
1111
obj_type = ContentType.objects.get_for_model(obj)
12-
news = self.create(content_type=obj_type, object_id=obj.id, **kwargs)
12+
news = self.create(content_type=obj_type, object_id=obj.pk, **kwargs)
1313
return news
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Generated by Django 4.2.3 on 2023-07-18 23:25
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("files", "0005_alter_userfile_options"),
11+
("contenttypes", "0002_remove_content_type_name"),
12+
("news", "0005_news_content_type_news_object_id"),
13+
]
14+
15+
operations = [
16+
migrations.AlterModelOptions(
17+
name="news",
18+
options={
19+
"ordering": ["-datetime_created"],
20+
"verbose_name": "Новость",
21+
"verbose_name_plural": "Новости",
22+
},
23+
),
24+
migrations.RemoveField(
25+
model_name="news",
26+
name="cover_url",
27+
),
28+
migrations.RemoveField(
29+
model_name="news",
30+
name="short_text",
31+
),
32+
migrations.RemoveField(
33+
model_name="news",
34+
name="tags",
35+
),
36+
migrations.RemoveField(
37+
model_name="news",
38+
name="title",
39+
),
40+
migrations.AddField(
41+
model_name="news",
42+
name="files",
43+
field=models.ManyToManyField(
44+
blank=True, related_name="news", to="files.userfile"
45+
),
46+
),
47+
migrations.AlterField(
48+
model_name="news",
49+
name="content_type",
50+
field=models.ForeignKey(
51+
on_delete=django.db.models.deletion.CASCADE,
52+
related_name="news",
53+
to="contenttypes.contenttype",
54+
),
55+
),
56+
migrations.AlterField(
57+
model_name="news",
58+
name="object_id",
59+
field=models.PositiveIntegerField(),
60+
),
61+
migrations.DeleteModel(
62+
name="NewsTag",
63+
),
64+
]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Generated by Django 4.2.3 on 2023-07-20 18:34
2+
import os
3+
4+
from django.db import migrations
5+
from django.db.migrations import RunPython
6+
7+
8+
def project_news_to_news_news(apps, schema_editor):
9+
ProjectNews = apps.get_model("projects.ProjectNews")
10+
News = apps.get_model("news", "News")
11+
ContentType = apps.get_model("contenttypes", "ContentType")
12+
content_type_count = ContentType.objects.all().count()
13+
if content_type_count == 0:
14+
return
15+
16+
project_news_content_type = ContentType.objects.get(
17+
app_label="projects", model="projectnews"
18+
)
19+
project_content_type = ContentType.objects.get(
20+
app_label="projects", model="project"
21+
)
22+
23+
news_content_type = ContentType.objects.get(app_label="news", model="news")
24+
Like = apps.get_model("core", "Like")
25+
View = apps.get_model("core", "View")
26+
for project_news in ProjectNews.objects.all():
27+
obj = News.objects.create(
28+
text=project_news.text,
29+
content_type=project_content_type,
30+
object_id=project_news.project.id,
31+
datetime_created=project_news.datetime_created,
32+
datetime_updated=project_news.datetime_updated
33+
)
34+
obj.files.set(project_news.files.all())
35+
likes = Like.objects.filter(
36+
content_type=project_news_content_type,
37+
object_id=project_news.id,
38+
)
39+
views = View.objects.filter(
40+
content_type=project_news_content_type,
41+
object_id=project_news.id,
42+
)
43+
for i in likes:
44+
i.content_type = news_content_type
45+
i.object_id = obj.id
46+
i.save()
47+
for i in views:
48+
i.content_type = news_content_type
49+
i.object_id = obj.id
50+
i.save()
51+
obj.save()
52+
53+
54+
class Migration(migrations.Migration):
55+
56+
dependencies = [
57+
("news", "0006_alter_news_options_remove_news_cover_url_and_more"),
58+
("projects", "0013_projectnews"),
59+
("projects", "0001_initial"),
60+
("projects", "0002_initial"),
61+
]
62+
63+
operations = [
64+
RunPython(project_news_to_news_news, reverse_code=migrations.RunPython.noop)
65+
]

news/mixins.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from news.models import News
2+
from partner_programs.models import PartnerProgram
3+
from projects.models import Project
4+
5+
6+
class NewsQuerysetMixin:
7+
"""
8+
Mixin for getting queryset for news
9+
"""
10+
11+
def get_queryset_for_project(self):
12+
try:
13+
project = Project.objects.get(pk=self.kwargs.get("project_pk"))
14+
except Project.DoesNotExist:
15+
return []
16+
return News.objects.get_news(obj=project)
17+
18+
def get_queryset_for_program(self):
19+
try:
20+
program = PartnerProgram.objects.get(pk=self.kwargs.get("partnerprogram_pk"))
21+
except PartnerProgram.DoesNotExist:
22+
return []
23+
return News.objects.get_news(obj=program)
24+
25+
def get_queryset(self):
26+
if self.kwargs.get("project_pk") is not None:
27+
# it's a project
28+
return self.get_queryset_for_project()
29+
elif self.kwargs.get("partnerprogram_pk") is not None:
30+
# it's a partner program
31+
return self.get_queryset_for_program()
32+
else:
33+
return []

news/models.py

Lines changed: 29 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,50 @@
1-
from django.contrib.contenttypes.fields import GenericForeignKey
1+
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
22
from django.contrib.contenttypes.models import ContentType
33
from django.db import models
44

5+
from core.models import Like, View
6+
from files.models import UserFile
57
from news.managers import NewsManager
68

79

8-
class NewsTag(models.Model):
9-
"""News tag model
10-
11-
Attributes:
12-
name: A CharField for the tag's name
13-
description: A CharField for the tag's description
14-
datetime_created: A DateTimeField indicating date of creation.
15-
datetime_updated: A DateTimeField indicating date of update.
16-
"""
17-
18-
name = models.CharField("Название тега", max_length=256, blank=False, null=False)
19-
# hopefully 512 characters are enough for any tag description
20-
description = models.CharField("Описание тега", max_length=512, blank=True, null=True)
21-
22-
# probably not really needed here but won't hurt
23-
datetime_created = models.DateTimeField(
24-
verbose_name="Дата создания", null=False, auto_now_add=True
25-
)
26-
datetime_updated = models.DateTimeField(
27-
verbose_name="Дата изменения", null=False, auto_now=True
28-
)
29-
30-
def __str__(self):
31-
return f"NewsTag<{self.id}> - {self.name}"
32-
33-
class Meta:
34-
verbose_name = "Тег"
35-
verbose_name_plural = "Теги"
36-
37-
3810
class News(models.Model):
39-
"""
40-
News model
41-
42-
Attributes:
43-
title: A CharField news title.
44-
text: A TextField news text content.
45-
short_text: A TextField news short text content.
46-
cover_url: A URLField link to news cover image.
47-
tags: A ManyToManyField listing all tags of this news object
48-
datetime_created: A DateTimeField indicating date of creation.
49-
datetime_updated: A DateTimeField indicating date of update.
50-
"""
51-
52-
title = models.CharField(max_length=256, null=False)
53-
text = models.TextField(null=False)
54-
short_text = models.TextField(max_length=256, blank=True)
55-
cover_url = models.URLField(null=False)
56-
57-
tags = models.ManyToManyField(NewsTag, blank=True, verbose_name="Список тегов")
58-
59-
# generic relation to project/program/ something else possibly
6011
content_type = models.ForeignKey(
61-
ContentType, on_delete=models.CASCADE, related_name="news", null=True, blank=True
12+
ContentType,
13+
on_delete=models.CASCADE,
14+
related_name="news",
6215
)
63-
object_id = models.PositiveIntegerField(null=True, blank=True)
16+
object_id = models.PositiveIntegerField()
6417
content_object = GenericForeignKey("content_type", "object_id")
6518

19+
text = models.TextField(
20+
null=False,
21+
blank=False,
22+
)
23+
files = models.ManyToManyField(UserFile, related_name="news", blank=True)
24+
25+
views = GenericRelation(
26+
View,
27+
related_query_name="project_views",
28+
)
29+
likes = GenericRelation(
30+
Like,
31+
related_query_name="project_news",
32+
)
33+
6634
datetime_created = models.DateTimeField(
67-
verbose_name="Дата создания", null=False, auto_now_add=True
35+
verbose_name="Дата создания",
36+
null=False,
37+
auto_now_add=True,
6838
)
6939
datetime_updated = models.DateTimeField(
70-
verbose_name="Дата изменения", null=False, auto_now=True
40+
verbose_name="Дата изменения",
41+
null=False,
42+
auto_now=True,
7143
)
7244

7345
objects = NewsManager()
7446

75-
@property
76-
def tags_str(self):
77-
"""Formats tags to string
78-
79-
Returns: List of tags' names separated by a comma
80-
"""
81-
return ", ".join([i.name for i in self.tags.all()])
82-
83-
def __str__(self):
84-
return f"News<{self.id}> - {self.title}"
85-
8647
class Meta:
8748
verbose_name = "Новость"
8849
verbose_name_plural = "Новости"
50+
ordering = ["-datetime_created"]

news/pagination.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from rest_framework import pagination
2+
3+
4+
class NewsPagination(pagination.LimitOffsetPagination):
5+
"""
6+
Pagination for News
7+
8+
For example:
9+
/news/?limit=10&offset=10
10+
gets the next 10 news after the first 10 news.
11+
"""
12+
13+
default_limit = 10
14+
limit_query_param = "limit"
15+
offset_query_param = "offset"

0 commit comments

Comments
 (0)