import datetime from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from api.extension import return_code from api import models # 必须认证成功之后才能访问 class TokenAuthentication(BaseAuthentication): def authenticate(self, request): token = request.query_params.get("token") if not token: raise AuthenticationFailed({"code": return_code.AUTH_FAILED, "error": "认证失败"}) user_object = models.UserInfo.objects.filter(token=token).first() if not user_object: raise AuthenticationFailed({"code": return_code.AUTH_FAILED, "error": "认证失败"}) # if datetime.datetime.now() > user_object.token_expiry_date: # raise AuthenticationFailed({"code": return_code.AUTH_OVERDUE, "error": "认证过期"}) return user_object, token def authenticate_header(self, request): return 'Bearer realm="API"' # 登录,可以访问 request.user # 不登录,也可以访问 request.user=None class UserAnonTokenAuthentication(BaseAuthentication): def authenticate(self, request): token = request.query_params.get("token") if not token: return None user_object = models.UserInfo.objects.filter(token=token).first() if not user_object: return None if datetime.datetime.now() > user_object.token_expiry_date: return None return user_object, token def authenticate_header(self, request): return 'Bearer realm="API"'
from rest_framework.filters import BaseFilterBackend class SelfFilterBackend(BaseFilterBackend): def filter_queryset(self, request, queryset, view): return queryset.filter(user=request.user)
from rest_framework import mixins from rest_framework.response import Response from api.extension import return_code class DigCreateModelMixin(mixins.CreateModelMixin): def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) # 1. 异常处理 if not serializer.is_valid(): return Response({"code": return_code.VALIDATE_ERROR, 'detail': serializer.errors}) # 2. 优化perform_create res = self.perform_create(serializer) # 3. 返回数据的处理 return res or Response({"code": return_code.SUCCESS, 'data': serializer.data}) class DigListModelMixin(mixins.ListModelMixin): def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return Response({"code": return_code.SUCCESS, 'data': serializer.data}) serializer = self.get_serializer(queryset, many=True) return Response({"code": return_code.SUCCESS, 'data': serializer.data}) class DigDestroyModelMixin(mixins.DestroyModelMixin): def destroy(self, request, *args, **kwargs): instance = self.get_object() res = self.perform_destroy(instance) return res or Response({"code": return_code.SUCCESS}) class DigUpdateModelMixin(mixins.UpdateModelMixin): def destroy(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) if not serializer.is_valid(): return Response({"code": return_code.VALIDATE_ERROR, 'detail': serializer.errors}) res = self.perform_update(serializer) return res or Response({"code": return_code.SUCCESS, 'data': serializer.data})
from rest_framework.pagination import LimitOffsetPagination class DigLimitOffsetPagination(LimitOffsetPagination): # xxxxxx?limit=10 default_limit = 10 max_limit = 100 offset_query_param = None
# 成功 SUCCESS = 0 # 用户提交数据校验失败 VALIDATE_ERROR = 1001 # 认证失败 AUTH_FAILED = 2000 # 认证过期 AUTH_OVERDUE = 2001 # 无权访问 PERMISSION_DENIED = 3000 # 无权访问 TOO_MANY_REQUESTS = 4000
from rest_framework import status from rest_framework.throttling import SimpleRateThrottle from django.core.cache import cache as default_cache from rest_framework import exceptions from api.extension import return_code class ThrottledException(exceptions.APIException): status_code = status.HTTP_429_TOO_MANY_REQUESTS default_code = 'throttled' class NewsCreateRateThrottle(SimpleRateThrottle): cache = default_cache # 访问记录存放在django的缓存中(需设置缓存) scope = "user" # 构造缓存中的key cache_format = 'throttle_%(scope)s_%(ident)s' # 设置访问频率,例如:1/5m THROTTLE_RATES = {"user": "1/5m"} def parse_rate(self, rate): if rate is None: return (None, None) num, period = rate.split('/') # "1/5m" num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[-1]] count = int(period[0:-1]) return (num_requests, duration * count) def get_cache_key(self, request, view): ident = request.user.pk return self.cache_format % {'scope': self.scope, 'ident': ident} def throttle_failure(self): wait = self.wait() detail = { "code": return_code.TOO_MANY_REQUESTS, "data": "访问频率限制", 'detail': "需等待{}秒后才能访问".format(int(wait)) } raise ThrottledException(detail) def throttle_success(self): # self.history.insert(0, self.now) # self.cache.set(self.key, self.history, self.duration) return True def done(self): """ 数据库中创建成功后,再来调用这个方法""" self.history.insert(0, self.now) self.cache.set(self.key, self.history, self.duration)
from rest_framework import serializers from rest_framework.exceptions import ValidationError from api import models class RegisterSerializer(serializers.ModelSerializer): confirm_password = serializers.CharField(label="确认密码", min_length=8, write_only=True) password = serializers.CharField(label="密码", min_length=8, write_only=True) class Meta: model = models.UserInfo fields = ['username', "phone", "password", "confirm_password"] def validate_username(self, value): exists = models.UserInfo.objects.filter(username=value, deleted=False).exists() if exists: raise ValidationError("用户名已存在") return value def validate_phone(self, value): exists = models.UserInfo.objects.filter(phone=value, deleted=False).exists() if exists: raise ValidationError("手机号已存在") return value def validate_confirm_password(self, value): password = self.initial_data.get('password') if password == value: return value raise ValidationError("两次密码不一致") class AuthSerializer(serializers.Serializer): username = serializers.CharField(label="用户名", write_only=True, required=False) # 不提交 phone = serializers.CharField(label="手机", write_only=True, required=False) # 不提交 password = serializers.CharField(label="密码", min_length=8, write_only=True) def validate_username(self, value): username = self.initial_data.get("username") phone = self.initial_data.get("phone") if not username and not phone: raise ValidationError("用户名或手机为空") if username and phone: raise ValidationError("提交数据异常") return value
from rest_framework import serializers from rest_framework.exceptions import ValidationError from api import models class CollectSubNewsSerializer(serializers.ModelSerializer): image_list = serializers.SerializerMethodField(read_only=True) class Meta: model = models.News fields = ['id', 'title', 'url', "image_list"] def get_image_list(self, obj): if not obj.image: return [] return obj.image.split(',') class CollectSerializer(serializers.ModelSerializer): news_info = CollectSubNewsSerializer(read_only=True, source="news") # news=2 class Meta: model = models.Collect fields = ['id', "news", "news_info"] extra_kwargs = {'news': {'write_only': True}} def validate_news(self, value): if value.deleted: raise ValidationError("资讯不存在") return value
from rest_framework import serializers from api import models """ 根评论 { "news":1, "content":"11111111111111111", "reply":null } 子评论 { "news":1, "content":"3.1111", "reply":3 } """ class CreateCommentSerializer(serializers.ModelSerializer): create_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", read_only=True) # 'news', "reply", "content" # 根评论:news、content,reply=null,depth=0,root=null # 子评论:news、content,reply=回复的评论ID,depth=回复的评论深度+1,root=读回复的评论root=null或depth=0 ==读回复的评论;===读回复的评论.root # descendant_update_datetime根评论最近的更新时间; class Meta: model = models.Comment fields = ['news', "reply", "content", 'depth', "create_datetime"] read_only_fields = ['depth', ] extra_kwargs = {'news': {'write_only': True}} class ListCommentSerializer(serializers.ModelSerializer): create_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S") children = serializers.SerializerMethodField() class Meta: model = models.Comment fields = ['create_datetime', "reply", "content", 'children'] def get_children(self, obj): # 获取当前根评论的所有的子孙评论(后台) descendant_queryset = models.Comment.objects.filter(root=obj).order_by('id') descendant_dict = {} """ { 11:{"reply": 2, children:[ 13->{,"reply": 11, children:[ 15:{"reply": 13, children:[ 16:{"reply": 15, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 4, "create_datetime": "2021-09-01 22:32:22"} ],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 3, "create_datetime": "2021-09-01 22:32:22"} ],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 2, "create_datetime": "2021-09-01 22:32:22"} ],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 1, "create_datetime": "2021-09-01 22:32:22"} 12:{"reply": 2, children:[ 14->{"reply": 12, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 2, "create_datetime": "2021-09-01 22:32:22"} ],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 1, "create_datetime": "2021-09-01 22:32:22"} 13:{"reply": 11, children:[ 15:{"reply": 13, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 3, "create_datetime": "2021-09-01 22:32:22"} ],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 2, "create_datetime": "2021-09-01 22:32:22"} 14:{"reply": 12, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 2, "create_datetime": "2021-09-01 22:32:22"} 15:{"reply": 13, children:[ 16:{"reply": 15, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 4, "create_datetime": "2021-09-01 22:32:22"} ],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 3, "create_datetime": "2021-09-01 22:32:22"} 16:{"reply": 15, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 4, "create_datetime": "2021-09-01 22:32:22"} } """ for descendant in descendant_queryset: ser = CreateCommentSerializer(instance=descendant, many=False) row = {'children': []} row.update(ser.data) descendant_dict[descendant.id] = row print(descendant_dict) # 根评论obj的1级评论 children_list = [ # # 11 # {"reply": 2, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 1, "create_datetime": "2021-09-01 22:32:22"}, # # 12 # {"reply": 2, children:[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 1, "create_datetime": "2021-09-01 22:32:22"} ] for cid, item in descendant_dict.items(): depth = item['depth'] if depth == 1: children_list.append(item) continue reply_id = item['reply'] descendant_dict[reply_id]['children'].append(item) return children_list
from rest_framework import serializers from rest_framework.exceptions import ValidationError from api import models class NewsSerializer(serializers.ModelSerializer): image_list = serializers.SerializerMethodField(read_only=True) topic_title = serializers.CharField(source="topic.title", read_only=True) zone_title = serializers.CharField(source="get_zone_display", read_only=True) status = serializers.CharField(source="get_status_display", read_only=True) # title、url、image、'topic', "zone" # - 只有title,只创建文本 + 分区不能是图片 # - 有title,image, # - 有title,url class Meta: model = models.News fields = ['id', "title", "url", 'image', 'topic', "zone", "zone_title", 'image_list', "topic_title", 'collect_count', 'recommend_count', 'comment_count', "status"] read_only_fields = ['collect_count', 'recommend_count', 'comment_count'] extra_kwargs = { 'topic': {'write_only': True}, # 新增时,topic=1 'image': {'write_only': True}, # 图片地址 xxxx,xxxx,xxxx 'zone': {'write_only': True}, } def get_image_list(self, obj): if not obj.image: return [] return obj.image.split(',') def validate_topic(self, value): if not value: return value request = self.context['request'] exists = models.Topic.objects.filter(deleted=False, id=value.id, user=request.user).exists() if not exists: raise ValidationError("话题不存在") return value def validate_title(self, value): url = self.initial_data.get('url') image = self.initial_data.get('image') zone = self.initial_data.get('zone') if url and image: raise ValidationError("请求数据错误") if not url and not image: if zone == 3: raise ValidationError("分区选择错误") return value def create(self, validated_data): request = self.context["request"] # 1.创建新闻资讯 new_object = models.News.objects.create(recommend_count=1, **validated_data) # 2.推荐记录 models.Recommend.objects.create( news=new_object, user=request.user ) return new_object class IndexSubTopicSerializer(serializers.ModelSerializer): class Meta: model = models.Topic fields = ['id', 'title', 'is_hot'] class IndexSubUserSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = ['id', 'username', ] class IndexSerializer(serializers.ModelSerializer): image_list = serializers.SerializerMethodField() collect = serializers.SerializerMethodField() recommend = serializers.SerializerMethodField() zone = serializers.CharField(source='get_zone_display') topic = IndexSubTopicSerializer(read_only=True) user = IndexSubUserSerializer(read_only=True) class Meta: model = models.News fields = ['id', "title", "url", 'image_list', 'topic', "zone", "user", 'collect', 'recommend', 'comment_count', ] def get_image_list(self, obj): if not obj.image: return [] return obj.image.split(',') def get_collect(self, obj): request = self.context['request'] if not request.user: return {'count': obj.collect_count, 'has_collect': False} exists = models.Collect.objects.filter(user=request.user, news=obj).exists() return {'count': obj.collect_count, 'has_collect': exists} def get_recommend(self, obj): request = self.context['request'] if not request.user: return {'count': obj.recommend_count, 'has_recommend': False} exists = models.Recommend.objects.filter(user=request.user, news=obj).exists() return {'count': obj.recommend_count, 'has_recommend': exists}
from rest_framework import serializers from rest_framework.exceptions import ValidationError from api import models class RecommendSubNewsSerializer(serializers.ModelSerializer): image_list = serializers.SerializerMethodField(read_only=True) class Meta: model = models.News fields = ['id', 'title', 'url', "image_list"] def get_image_list(self, obj): if not obj.image: return [] return obj.image.split(',') class RecommendSerializer(serializers.ModelSerializer): news_info = RecommendSubNewsSerializer(read_only=True, source="news") class Meta: model = models.Recommend fields = ['id', "news", "news_info"] extra_kwargs = {'news': {'write_only': True}}
from rest_framework import serializers from rest_framework.exceptions import ValidationError from api import models class TopicSerializer(serializers.ModelSerializer): class Meta: model = models.Topic fields = ['id', "title", "is_hot"] # read_only_fields = ['is_hot'] extra_kwargs = {'is_hot': {'read_only': True}}
import uuid import datetime from django.db.models import Q from django.urls import reverse from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from api.extension.mixins import DigCreateModelMixin from api.serializers.account import RegisterSerializer, AuthSerializer from api.extension import return_code from api import models from rest_framework.throttling import SimpleRateThrottle from rest_framework.mixins import CreateModelMixin """ 1. 只需要提供POST方法 2. 请求进来执行 DigCreateModelMixin的create方法 3. 获取数据request.data,进行校验(RegisterSerializer) """ class RegisterView(DigCreateModelMixin, GenericViewSet): """ 用户注册 """ authentication_classes = [] permission_classes = [] serializer_class = RegisterSerializer def perform_create(self, serializer): serializer.validated_data.pop('confirm_password') # super().perform_create(serializer) serializer.save() class AuthView(APIView): """ 用户登录 """ authentication_classes = [] permission_classes = [] # 2. 数据库校验用户名和密码的合法性 def post(self, request): # 1. 获取用户请求 & 校验 serializer = AuthSerializer(data=request.data) if not serializer.is_valid(): # { 'username':[错误信息,], 'phone':[xxxx,]} return Response({"code": return_code.VALIDATE_ERROR, 'detail': serializer.errors}) username = serializer.validated_data.get('username') phone = serializer.validated_data.get('phone') password = serializer.validated_data.get('password') user_object = models.UserInfo.objects.filter(Q(Q(username=username) | Q(phone=phone)), password=password).first() if not user_object: return Response({"code": return_code.VALIDATE_ERROR, "error": "用户名或密码错误"}) token = str(uuid.uuid4()) user_object.token = token # 设置token有效期:当前时间 + 2周 user_object.token_expiry_date = datetime.datetime.now() + datetime.timedelta(weeks=2) user_object.save() return Response({"code": return_code.SUCCESS, "data": {"token": token, "name": user_object.username}})
from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from django_filters import FilterSet, filters from django_filters.rest_framework import DjangoFilterBackend from api import models from api.serializers.collect import CollectSerializer from api.extension.filter import SelfFilterBackend from api.extension.mixins import DigCreateModelMixin, DigListModelMixin from api.extension import return_code class CollectFilterSet(FilterSet): latest_id = filters.NumberFilter(field_name='id', lookup_expr='lt') class Meta: model = models.Collect fields = ["latest_id", ] class CollectView(DigCreateModelMixin, DigListModelMixin, GenericViewSet): """ 收藏接口 """ filter_backends = [SelfFilterBackend, DjangoFilterBackend] filterset_class = CollectFilterSet # 当前登录用户的所有收藏记录 queryset = models.Collect.objects serializer_class = CollectSerializer def perform_create(self, serializer): user = self.request.user instance = models.Collect.objects.filter(user=user, **serializer.validated_data).first() if not instance: instance = serializer.save(user=user) instance.news.collect_count += 1 instance.news.save() return Response({"code": return_code.SUCCESS, 'data': {'active': True}}) else: instance.delete() instance.news.collect_count -= 1 instance.news.save() return Response({"code": return_code.SUCCESS, 'data': {'active': False}})
import datetime from rest_framework.request import Request from rest_framework.viewsets import GenericViewSet from rest_framework.permissions import BasePermission from rest_framework.exceptions import PermissionDenied from django_filters import FilterSet, filters from django_filters.rest_framework import DjangoFilterBackend from api import models from api.serializers.comment import CreateCommentSerializer, ListCommentSerializer from api.extension.filter import SelfFilterBackend from api.extension.mixins import DigCreateModelMixin, DigListModelMixin, DigDestroyModelMixin, DigUpdateModelMixin from api.extension.auth import UserAnonTokenAuthentication, TokenAuthentication from api.extension import return_code class CommentFilterSet(FilterSet): news = filters.NumberFilter(field_name='news', required=True) latest_id = filters.DateTimeFilter(field_name='descendant_update_datetime', lookup_expr='lte') class Meta: model = models.Comment fields = ["latest_id", 'news'] """ 1.先根据后代的更新时间,进行排序,获取根评论 10。 2.这些根评论关联的子评论,并构造父子关系。 """ class CommentView(DigListModelMixin, DigCreateModelMixin, GenericViewSet): """ 评论 """ filter_backends = [DjangoFilterBackend] filterset_class = CommentFilterSet authentication_classes = [TokenAuthentication, ] # ?news=2 -> news=2 # 获取某条新闻资讯的 根评论(根据后代更新时间排序) queryset = models.Comment.objects.filter(depth=0).order_by("-descendant_update_datetime") serializer_class = CreateCommentSerializer def perform_create(self, serializer): print(serializer.initial_data) #{'news': 1, 'content': '...', 'reply': 2} print(serializer.validated_data) # OrderedDict([('news', <News: News object (1)>), ('reply', <Comment: Comment object (2)>), ('content', '...')]) reply = serializer.validated_data.get('reply') #reply是对象 if not reply: # 如果是根评论 'news', "reply", "content" """ {"news":1, "content":"111", "reply":null } """ instance = serializer.save(user=self.request.user) else: # 如果子评论 """ {"news":1, "content":"111", "reply":2 } """ # 1.获取根评论 if not reply.root: # 给根评论回复 root = reply else: root = reply.root # 创建评论 instance = serializer.save(user=self.request.user, depth=reply.depth + 1, root=root) # 根评论的最新更新时间 root.descendant_update_datetime = datetime.datetime.now() root.save() instance.news.comment_count += 1 instance.news.save() def get_serializer_class(self): if self.request.method == "GET": return ListCommentSerializer return CreateCommentSerializer def get_authenticators(self): if self.request.method == "POST": return super().get_authenticators() return []
from rest_framework.viewsets import GenericViewSet from django_filters import FilterSet, filters from django_filters.rest_framework import DjangoFilterBackend from api import models from api.serializers.news import NewsSerializer, IndexSerializer from api.extension.filter import SelfFilterBackend from api.extension.mixins import DigCreateModelMixin, DigListModelMixin from api.extension.auth import UserAnonTokenAuthentication from api.extension.throttle import NewsCreateRateThrottle class NewsFilterSet(FilterSet): latest_id = filters.NumberFilter(field_name='id', lookup_expr='lt') class Meta: model = models.News fields = ["latest_id", ] class NewsView(DigListModelMixin, DigCreateModelMixin, GenericViewSet): filter_backends = [SelfFilterBackend, DjangoFilterBackend] filterset_class = NewsFilterSet # 未删除 & 属于当前用户创建的新闻资讯 queryset = models.News.objects.filter(deleted=False).order_by('-id') serializer_class = NewsSerializer # 自定义的类变量 throttle_objects = [NewsCreateRateThrottle(), ] def perform_create(self, serializer): # 1.创建新闻资讯 # 2.自己对自己的内容做推荐 # - 推荐数量+1 # - 推荐记录 用户&资讯 serializer.save(user=self.request.user) # 数据库中已增加成功,调用限流的那个done方法 for throttle in self.get_throttles(): throttle.done() def get_throttles(self): if self.request.method == "POST": return self.throttle_objects return [] class IndexFilterSet(FilterSet): latest_id = filters.NumberFilter(field_name='id', lookup_expr='lt') class Meta: model = models.News fields = ["latest_id", 'zone'] # ?zone=1 # ?latest_id=99&limit=10 class IndexView(DigListModelMixin, GenericViewSet): filter_backends = [DjangoFilterBackend] filterset_class = IndexFilterSet authentication_classes = [UserAnonTokenAuthentication, ] # queryset = models.News.objects.filter(deleted=False, status=2).order_by('-id') queryset = models.News.objects.filter(deleted=False).order_by('-id') serializer_class = IndexSerializer
from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from django_filters import FilterSet, filters from django_filters.rest_framework import DjangoFilterBackend from api import models from api.serializers.recommend import RecommendSerializer from api.extension.filter import SelfFilterBackend from api.extension.mixins import DigCreateModelMixin, DigListModelMixin from api.extension import return_code class RecommendFilterSet(FilterSet): latest_id = filters.NumberFilter(field_name='id', lookup_expr='lt') class Meta: model = models.Recommend fields = ["latest_id", ] class RecommendView(DigCreateModelMixin, DigListModelMixin, GenericViewSet): """ 推荐接口 """ filter_backends = [SelfFilterBackend, DjangoFilterBackend] filterset_class = RecommendFilterSet queryset = models.Recommend.objects serializer_class = RecommendSerializer def perform_create(self, serializer): user = self.request.user instance = models.Recommend.objects.filter(user=user, **serializer.validated_data).first() if not instance: instance = serializer.save(user=user) instance.news.recommend_count += 1 instance.news.save() return Response({"code": return_code.SUCCESS, 'data': {'active': True}}) else: instance.delete() instance.news.recommend_count -= 1 instance.news.save() return Response({"code": return_code.SUCCESS, 'data': {'active': False}})
#!/usr/bin/env python # -*- coding: utf-8 -*- from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response class TestView(APIView): authentication_classes = [] def post(self, request): hobbies=request.data.get('hobbies') print(hobbies,type(hobbies)) return Response('ok')
from rest_framework.viewsets import GenericViewSet from django_filters import FilterSet, filters from django_filters.rest_framework import DjangoFilterBackend from api import models from api.serializers.topic import TopicSerializer from api.extension.filter import SelfFilterBackend from api.extension.mixins import DigCreateModelMixin, DigListModelMixin, DigDestroyModelMixin, DigUpdateModelMixin class TopicFilterSet(FilterSet): # ?latest_id=99 -> id<99 # ?latest_id=99&limit=10 -> id<99 limit 10 latest_id = filters.NumberFilter(field_name='id', lookup_expr='lt') class Meta: model = models.Topic fields = ["latest_id", ] class TopicView(DigListModelMixin, DigCreateModelMixin, DigDestroyModelMixin, DigUpdateModelMixin, GenericViewSet): """ 主题 """ # 当前登录用户的调教 filter_backends = [SelfFilterBackend, DjangoFilterBackend] filterset_class = TopicFilterSet queryset = models.Topic.objects.filter(deleted=False).order_by('-id') serializer_class = TopicSerializer def perform_create(self, serializer): serializer.save(user=self.request.user) def perform_destroy(self, instance): instance.deleted = True instance.save()
from django.apps import AppConfig class ApiConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'api'
from django.db import models class DeletedModel(models.Model): deleted = models.BooleanField(verbose_name="已删除", default=False) class Meta: abstract = True class UserInfo(DeletedModel): """ 用户表 """ username = models.CharField(verbose_name="用户名", max_length=32) phone = models.CharField(verbose_name="手机号", max_length=32, db_index=True) password = models.CharField(verbose_name="密码", max_length=64) token = models.CharField(verbose_name="token", max_length=64, null=True, blank=True, db_index=True) token_expiry_date = models.DateTimeField(verbose_name="token有效期", null=True, blank=True) status_choice = ( (1, "激活"), (2, "禁用"), ) status = models.IntegerField(verbose_name="状态", choices=status_choice, default=1) create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) class Meta: # The newer indexes option provides more functionality than index_together # index_together may be deprecated in the future. # https://docs.djangoproject.com/en/3.2/ref/models/options/#index-together indexes = [ models.Index(fields=['username', "password"], name='idx_name_pwd') ] class Topic(DeletedModel): """ 话题 """ title = models.CharField(verbose_name="话题", max_length=16, db_index=True) is_hot = models.BooleanField(verbose_name="热门话题", default=False) user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE) create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) class News(DeletedModel): """ 新闻资讯 """ zone_choices = ((1, "42区"), (2, "段子"), (3, "图片"), (4, "挨踢1024"), (5, "你问我答")) zone = models.IntegerField(verbose_name="专区", choices=zone_choices) title = models.CharField(verbose_name="文字", max_length=150) url = models.CharField(verbose_name="链接", max_length=200, null=True, blank=True) # xxxxx?xxxxxx.png,xxxxxxxx.jeg image = models.TextField(verbose_name="图片地址", help_text="逗号分割", null=True, blank=True) topic = models.ForeignKey(verbose_name="话题", to="Topic", on_delete=models.CASCADE, null=True, blank=True) user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE) create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) status_choice = ( (1, "待审核"), (2, "已通过"), (3, "未通过"), ) status = models.IntegerField(verbose_name="状态", choices=status_choice, default=1) collect_count = models.IntegerField(verbose_name="收藏数", default=0) recommend_count = models.IntegerField(verbose_name="推荐数", default=0) comment_count = models.IntegerField(verbose_name="评论数", default=0) class Collect(models.Model): """ 收藏 """ news = models.ForeignKey(verbose_name="资讯", to="News", on_delete=models.CASCADE) user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE) create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) class Meta: # unique_together = [['news', 'user']] constraints = [ models.UniqueConstraint(fields=['news', 'user'], name='uni_collect_news_user') ] class Recommend(models.Model): """ 推荐 """ news = models.ForeignKey(verbose_name="资讯", to="News", on_delete=models.CASCADE) user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE) create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) class Meta: constraints = [ models.UniqueConstraint(fields=['news', 'user'], name='uni_recommend_news_user') ] class Comment(models.Model): """ 评论表 """ news = models.ForeignKey(verbose_name="资讯", to="News", on_delete=models.CASCADE) user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE) content = models.CharField(verbose_name="内容", max_length=150) depth = models.IntegerField(verbose_name="深度", default=0) root = models.ForeignKey(verbose_name="根评论", to="Comment", related_name="descendant", on_delete=models.CASCADE, null=True, blank=True) reply = models.ForeignKey(verbose_name="回复", to="Comment", related_name="reply_list", on_delete=models.CASCADE, null=True, blank=True) create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) # 针对根评论 descendant_update_datetime = models.DateTimeField(verbose_name="后代更新时间", auto_now_add=True)
from django.urls import path from rest_framework import routers from api.views import account, topic, news, collect, recommend, comment,test # urlpatterns = [ # # /api/register/ # path('register/', account.RegisterView.as_view({"post": "create"})), # ] # router = routers.SimpleRouter() # # # /api/register/ # # x-list # # x-create # router.register(r'register', account.RegisterView) # # urlpatterns = [ # # ] # urlpatterns += router.urls router = routers.SimpleRouter() router.register(r'register', account.RegisterView, 'register') # 创建话题(认证) router.register(r'topic', topic.TopicView) # 我的资讯 router.register(r'news', news.NewsView) # 资讯首页 router.register(r'index', news.IndexView) # 收藏 router.register(r'collect', collect.CollectView) # 推荐 router.register(r'recommend', recommend.RecommendView) # 评论 router.register(r'comment', comment.CommentView) urlpatterns = [ # path('register/', account.RegisterView.as_view({"post": "create"})), path('auth/', account.AuthView.as_view()), path('test/', test.TestView.as_view()), ] urlpatterns += router.urls
""" Django settings for dig project. Generated by 'django-admin startproject' using Django 3.2.6. For more information on this file, see https://docs.djangoproject.com/en/3.2/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.2/ref/settings/ """ import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-xi3i8jw=!smfwo%pg$$w1y5a^#wqk)&tc_!oc8_7^jqtf#4r@w' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_filters', 'api.apps.ApiConfig' ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'dig.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'dig.wsgi.application' # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/3.2/topics/i18n/ # LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh-hans' # datetime.datetime.now() / datetime.datetime.utcnow() => utc时间 # TIME_ZONE = 'UTC' # datetime.datetime.now() - 东八区时间 / datetime.datetime.utcnow() => utc时间 TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True # 影响自动生成数据库时间字段; # USE_TZ = True,创建UTC时间写入到数据库。 # USE_TZ = False,根据TIME_ZONE设置的时区进行创建时间并写入数据库 USE_TZ = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.2/howto/static-files/ STATIC_URL = '/static/' # Default primary key field type # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' from rest_framework.versioning import AcceptHeaderVersioning REST_FRAMEWORK = { # 版本配置 "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning", "DEFAULT_VERSION": "v1", "ALLOWED_VERSIONS": ["v1"], "VERSION_PARAM": "version", # # 认证配置 "DEFAULT_AUTHENTICATION_CLASSES": ["api.extension.auth.TokenAuthentication", ], "UNAUTHENTICATED_USER": lambda: None, "UNAUTHENTICATED_TOKEN": lambda: None, # # 分页配置 "DEFAULT_PAGINATION_CLASS": "api.extension.page.DigLimitOffsetPagination" } CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD": "", } } }
"""dig URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/3.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path, include urlpatterns = [ path('api/', include('api.urls')), ]
django-filter==2.4.0 django-redis==5.0.0 djangorestframework==3.12.4 # info = { # 11:{"reply": 2, "children":[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 1, "create_datetime": "2021-09-01 22:32:22"}, # 12:{"reply": 2, "children":[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 1, "create_datetime": "2021-09-01 22:32:22"}, # 13:{"reply": 11, "children":[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 2, "create_datetime": "2021-09-01 22:32:22"}, # 14:{"reply": 12, "children":[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 2, "create_datetime": "2021-09-01 22:32:22"}, # 15:{"reply": 13, "children":[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 3, "create_datetime": "2021-09-01 22:32:22"}, # 16:{"reply": 15, "children":[],"content": "oooadfa;skdjf;akjsd;flkjasdf","depth": 4, "create_datetime": "2021-09-01 22:32:22"} # } # # # data_list = [] # # data_list.append(info[11]) # data_list.append(info[12]) # # info[11]['children'].append(666) # # print(data_list) #################### descendant_dict = { 3: {'children': [], 'reply': 1, 'content': '1-1评论', 'depth': 1, 'create_datetime': '2021-09-01 11:26:04'}, 4: {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2021-09-01 11:26:16'}, 5: {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2021-09-01 22:16:40'}, 6: {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2021-09-01 22:17:02'}, 9: {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2022-01-03 22:32:08'}, 10: {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2022-01-03 22:32:38'}, 12: {'children': [], 'reply': 3, 'content': '3.1111', 'depth': 2, 'create_datetime': '2022-01-03 22:40:28'} } children_list = [] for cid, item in descendant_dict.items(): depth = item['depth'] if depth == 1: children_list.append(item) # continue reply_id = item['reply'] descendant_dict[reply_id]['children'].append(item) # li = [ # {'children': [ # {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2021-09-01 11:26:16'}, # {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2021-09-01 22:16:40'}, # {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2021-09-01 22:17:02'}, # {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2022-01-03 22:32:08'}, # {'children': [], 'reply': 3, 'content': '3-1评论', 'depth': 2, 'create_datetime': '2022-01-03 22:32:38'}, # {'children': [], 'reply': 3, 'content': '3.1111', 'depth': 2, 'create_datetime': '2022-01-03 22:40:28'} # ], # 'reply': 1, 'content': '1-1评论', 'depth': 1, 'create_datetime': '2021-09-01 11:26:04'} # ] print(children_list)