Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions .idea/django-angular-blog.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 103 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.idea/
BlogBackendProject/private.py
Empty file added backend/app/__init__.py
Empty file.
Empty file added backend/app/article/__init__.py
Empty file.
15 changes: 15 additions & 0 deletions backend/app/article/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.contrib import admin

# Register your models here.
from .models import ArticleInfo, ArticleDetail

#
# class ArticleinfoInline(admin.TabularInline):
# model = ArticleDetail
#
# # @admin.register(ArticleInfo)
# class ArticleDetail(admin.ModelAdmin):
# inlines = [ArticleinfoInline]

admin.site.register(ArticleInfo)
admin.site.register(ArticleDetail)
5 changes: 5 additions & 0 deletions backend/app/article/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class ArticleConfig(AppConfig):
name = 'app.article'
19 changes: 19 additions & 0 deletions backend/app/article/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.db.models import Q
import django_filters
from .models import ArticleInfo


class ArticleFilter(django_filters.rest_framework.FilterSet):

time_min = django_filters.DateFilter(field_name='add_time', lookup_expr='gte')
time_max = django_filters.DateFilter(field_name='add_time', lookup_expr='lte')

top_category = django_filters.NumberFilter(method='top_category_filter')

def top_category_filter(self, queryset, name, value):
return queryset.filter(Q(category_id=value) | Q(category__parent_category_id=value) | Q(
category__parent_category__parent_category_id=value))

class Meta:
model = ArticleInfo
fields = ['time_min', 'time_max', 'is_hot', 'is_recommend', 'is_banner']
34 changes: 34 additions & 0 deletions backend/app/article/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.0.5 on 2020-04-19 09:08

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('root', '0002_auto_20200418_1841'),
]

operations = [
migrations.CreateModel(
name='ArticleInfo',
fields=[
('postbaseinfo_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='root.PostBaseInfo')),
],
bases=('root.postbaseinfo',),
),
migrations.CreateModel(
name='ArticleDetail',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('origin_content', models.TextField(blank=True)),
('formatted_content', models.TextField()),
('add_time', models.DateTimeField(blank=True, null=True)),
('update_time', models.DateTimeField(blank=True, null=True)),
('article_info', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='details', to='article.ArticleInfo')),
],
),
]
Empty file.
40 changes: 40 additions & 0 deletions backend/app/article/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django.db import models

# Create your models here.
from django.db import models
import markdown

from app.root.models import Category, Tag, PostBaseInfo
from app.base.utils import MARKDOWN_EXTENSIONS


class ArticleInfo(PostBaseInfo):

def save(self, *args, **kwargs):
self.post_type = 'article'
super(ArticleInfo, self).save(*args, **kwargs)

def __str__(self):
return self.title

class Meta:
pass



class ArticleDetail(models.Model):
article_info = models.ForeignKey(ArticleInfo, null=True, blank=True, related_name='details',on_delete=models.CASCADE)
origin_content = models.TextField(null=False, blank=True)
formatted_content = models.TextField()
add_time = models.DateTimeField(null=True, blank=True)
update_time = models.DateTimeField(null=True, blank=True,)

def save(self, *args, **kwargs):
self.formatted_content = markdown.markdown(self.origin_content, extensions=MARKDOWN_EXTENSIONS,lazy_ol=False)
super(ArticleDetail, self).save(*args, **kwargs)

def __str__(self):
return self.article_info.title

class Meta:
pass
48 changes: 48 additions & 0 deletions backend/app/article/serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# _*_ coding: utf-8 _*_

from rest_framework import serializers

from .models import ArticleInfo, ArticleDetail
from app.root.serializer import SingleLevelCategorySerializer, TagSerializer, LicenseSerializer
from django.conf import settings


class ArticleDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ArticleDetail
fields = ( 'formatted_content', 'add_time', 'update_time')


class ArticleDetailInfoSerializer(serializers.ModelSerializer):
category = SingleLevelCategorySerializer()
tags = TagSerializer(many=True)
license = LicenseSerializer()
details = ArticleDetailSerializer(many=True)
browse_auth = serializers.CharField(required=False, max_length=100, write_only=True)

class Meta:
model = ArticleInfo
exclude = ('browse_password', 'browse_password_encrypt')


class ArticleBaseInfoSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
front_image = serializers.SerializerMethodField()
need_auth = serializers.SerializerMethodField()

def get_front_image(self, article):
if article.front_image:
return "{0}/{1}".format(settings.MEDIA_URL_PREFIX, article.front_image)

def get_need_auth(self, article):
if article.browse_password_encrypt:
return True
else:
return False

class Meta:
model = ArticleInfo
fields = (
'id', 'title', 'desc', 'author', 'tags', 'click_num', 'like_num', 'comment_num', 'post_type',
'front_image', 'is_recommend', 'is_hot', 'is_banner', 'is_commentable', 'need_auth',
'front_image_type', 'index', 'add_time')
3 changes: 3 additions & 0 deletions backend/app/article/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
58 changes: 58 additions & 0 deletions backend/app/article/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django.shortcuts import render


from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, filters, status, mixins
from rest_framework.response import Response

from .models import ArticleInfo
from .serializer import ArticleBaseInfoSerializer, ArticleDetailInfoSerializer
from .filters import ArticleFilter
from app.base.utils import CustomeLimitOffsetPagination


class ArticleBaseInfoListViewset(viewsets.ReadOnlyModelViewSet):
queryset = ArticleInfo.objects.filter(is_active=True)
serializer_class = ArticleBaseInfoSerializer

filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
filter_class = ArticleFilter
search_fields = ('title', 'subtitle', 'abstract', 'desc')
ordering_fields = ('click_num', 'like_num', 'comment_num', 'index', 'add_time')
ordering = ('-index', '-add_time')
pagination_class = CustomeLimitOffsetPagination

def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.click_num += 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)


class ArticleDetailInfoListViewset(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = ArticleInfo.objects.filter(is_active=True)
serializer_class = ArticleDetailInfoSerializer

filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
search_fields = ('title', 'subtitle', 'abstract', 'desc')
ordering_fields = ('click_num', 'like_num', 'comment_num', 'add_time')

pagination_class = CustomeLimitOffsetPagination

def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
if instance.browse_password_encrypt:
browse_auth = ""
if 'browse_auth' in request.query_params:
browse_auth = request.query_params['browse_auth']
if browse_auth != instance.browse_password_encrypt:
context = {
"error": "Protected Article 401 UNAUTHORIZED"
}
return Response(context, status=status.HTTP_401_UNAUTHORIZED)

instance.click_num += 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)
Empty file added backend/app/base/__init__.py
Empty file.
4 changes: 4 additions & 0 deletions backend/app/base/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.contrib import admin
from .models import SiteInfo
# Register your models here.
admin.site.register(SiteInfo)
5 changes: 5 additions & 0 deletions backend/app/base/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class BaseConfig(AppConfig):
name = 'app.base'
Loading