diff --git a/backend/constants.py b/backend/constants.py index dec1d2923cb17144c0b78a83e850a975dd3a5aab..f0bff253f72e78ffdab616dbced9fa9914136b70 100644 --- a/backend/constants.py +++ b/backend/constants.py @@ -5,4 +5,6 @@ POST_CATEGORIES = ( ) # 手机号码正则 -REGEX_MOBILE = '^1[358]\d{9}$|^147\d{8}$|^176\d{8}$' +REGEX_MOBILE = r'^1[358]\d{9}$|^147\d{8}$|^176\d{8}$' +REGEX_EMAIL = r'^.+@.+$' + diff --git a/backend/exceptions.py b/backend/exceptions.py index 213106fedb14c5c7ad6a2f5cdf8844c7281a9970..d8eda6075e852c159ee16aa5d4bd7489cfcb6b65 100644 --- a/backend/exceptions.py +++ b/backend/exceptions.py @@ -1,8 +1,8 @@ +from django.core.exceptions import ObjectDoesNotExist from django.db import DataError from rest_framework import status from rest_framework.exceptions import ValidationError from rest_framework.response import Response -from rest_framework.utils import json from rest_framework.views import exception_handler @@ -12,21 +12,27 @@ def custom_exception_handler(exc, handler): if response is None: if isinstance(exc, DataError): response = Response(data={ - 'code': '500', + 'code': 500, 'error_msg': '数据库异常', }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) if isinstance(exc, Exception): response = Response(data={ - 'code': '500', + 'code': 500, 'msg': '有未处理的异常', 'error_msg': exc.args, }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) if isinstance(exc, ValidationError): response = Response(data={ - 'code': '400', + 'code': 400, 'error_msg': exc.detail, }, status=status.HTTP_400_BAD_REQUEST) + if isinstance(exc, ObjectDoesNotExist): + response = Response(data={ + 'code': 400, + 'error_msg': exc.args, + }, status=status.HTTP_400_BAD_REQUEST) + return response diff --git a/backend/settings.py b/backend/settings.py index adb88d8d41692684f0c212b153bb17058d67485f..d9e362540e90693f2edfda8daefa2c4b7f38a40b 100644 --- a/backend/settings.py +++ b/backend/settings.py @@ -44,6 +44,8 @@ INSTALLED_APPS = [ 'operations', 'comments', 'posts', + 'subjects', + 'favorite', # --- DRF --- 'rest_framework', diff --git a/backend/urls.py b/backend/urls.py index 7673823d3beb9dce8b6530d3798e8526927c7939..de7459e598a8e4248e7c26f3040889ad7fdc08cf 100644 --- a/backend/urls.py +++ b/backend/urls.py @@ -43,6 +43,8 @@ urlpatterns = [ path('user-manage/', include('users.urls')), path('comment-manage/', include('comments.urls')), path('operation-manage/', include('operations.urls')), + path('subject-manage/', include('subjects.urls')), + path('favorite-manage/', include('favorite.urls')), ] # drf-yasg Swagger diff --git a/comments/migrations/0001_initial.py b/comments/migrations/0001_initial.py index a20d83e3e975c403e860637cb63700bfeaff29e4..6b713ef0d89e6152dbf35dd597ab3ae3ce7e0965 100644 --- a/comments/migrations/0001_initial.py +++ b/comments/migrations/0001_initial.py @@ -1,6 +1,7 @@ -# Generated by Django 3.2.6 on 2022-03-05 10:04 +# Generated by Django 3.2.6 on 2022-03-22 05:26 from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -8,6 +9,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('users', '0002_alter_userinfo_phone'), ] operations = [ @@ -20,6 +22,7 @@ class Migration(migrations.Migration): ('content', models.TextField(verbose_name='内容')), ('approved', models.BigIntegerField(default=0, verbose_name='赞成')), ('category', models.CharField(max_length=20, null=True, verbose_name='种类')), + ('author', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='comment_set', to='users.userinfo', verbose_name='评论者')), ], ), ] diff --git a/conda.yaml b/conda.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b287b4ae2e246d4c9e8ebbcd6b3d03997a42471a --- /dev/null +++ b/conda.yaml @@ -0,0 +1,52 @@ +name: E:\conda\qiusuo +channels: + - defaults +dependencies: + - ca-certificates=2022.2.1=haa95532_0 + - certifi=2021.10.8=py39haa95532_2 + - openssl=1.1.1m=h2bbff1b_0 + - pip=21.2.4=py39haa95532_0 + - python=3.9.7=h6244533_1 + - setuptools=58.0.4=py39haa95532_0 + - sqlite=3.37.2=h2bbff1b_0 + - tzdata=2021e=hda174b7_0 + - vc=14.2=h21ff451_1 + - vs2015_runtime=14.27.29016=h5e58377_2 + - wheel=0.37.1=pyhd3eb1b0_0 + - wincertstore=0.2=py39haa95532_2 + - pip: + - asgiref==3.5.0 + - autopep8==1.5.6 + - charset-normalizer==2.0.12 + - coreapi==2.3.3 + - coreschema==0.0.4 + - django==3.2.6 + - django-cors-headers==3.11.0 + - django-filter==21.1 + - djangorestframework==3.13.1 + - djangorestframework-jwt==1.11.0 + - docopt==0.6.2 + - drf-yasg==1.20.0 + - idna==3.3 + - inflection==0.5.1 + - itypes==1.2.0 + - jinja2==3.0.3 + - markupsafe==2.1.0 + - mysqlclient==2.1.0 + - packaging==21.3 + - pipreqs==0.4.11 + - pycodestyle==2.7.0 + - pyjwt==1.7.1 + - pymysql==1.0.2 + - pyparsing==3.0.7 + - pytz==2021.3 + - requests==2.27.1 + - ruamel.yaml==0.17.21 + - ruamel.yaml.clib==0.2.6 + - sqlparse==0.4.2 + - toml==0.10.2 + - uritemplate==4.1.1 + - urllib3==1.26.8 + - yarg==0.1.9 +prefix: E:\conda\qiusuo + diff --git a/favorite/__init__.py b/favorite/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/favorite/admin.py b/favorite/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/favorite/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/favorite/apps.py b/favorite/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..0d28f364bb096f9a2bbb598b4d00efd0f25938b7 --- /dev/null +++ b/favorite/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class FavoriteConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'favorite' diff --git a/favorite/migrations/0001_initial.py b/favorite/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..0a99c96f7e71cd398247511a293b11f79ae596f9 --- /dev/null +++ b/favorite/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.6 on 2022-03-22 14:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('users', '0002_alter_userinfo_phone'), + ('posts', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='UserFavoriteFolder', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, verbose_name='文件夹名字')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorite_folder_set', to='users.userinfo', verbose_name='所关联的用户')), + ], + ), + migrations.CreateModel( + name='UserFavorite', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('folder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorite_set', to='favorite.userfavoritefolder', verbose_name='所关联的文件夹')), + ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='posts.post', verbose_name='所关联的文章')), + ], + ), + ] diff --git a/favorite/migrations/0002_userfavoritefolder_create_at.py b/favorite/migrations/0002_userfavoritefolder_create_at.py new file mode 100644 index 0000000000000000000000000000000000000000..d224f04ea9344ff9ecbf8478ff6ca9c83c73e676 --- /dev/null +++ b/favorite/migrations/0002_userfavoritefolder_create_at.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.6 on 2022-03-23 03:32 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('favorite', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='userfavoritefolder', + name='create_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + ] diff --git a/favorite/migrations/0003_auto_20220323_1329.py b/favorite/migrations/0003_auto_20220323_1329.py new file mode 100644 index 0000000000000000000000000000000000000000..7177a73b11d4fa59a1a255a6ef047cd4c5fb5ba4 --- /dev/null +++ b/favorite/migrations/0003_auto_20220323_1329.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.6 on 2022-03-23 05:29 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('posts', '0001_initial'), + ('favorite', '0002_userfavoritefolder_create_at'), + ] + + operations = [ + migrations.RemoveField( + model_name='userfavoritefolder', + name='name', + ), + migrations.AddField( + model_name='userfavoritefolder', + name='folder_name', + field=models.CharField(max_length=20, null=True, verbose_name='收藏文件夹名字'), + ), + migrations.AlterField( + model_name='userfavorite', + name='post', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_post', to='posts.post', verbose_name='收藏所关联的文章'), + ), + ] diff --git a/favorite/migrations/__init__.py b/favorite/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/favorite/models.py b/favorite/models.py new file mode 100644 index 0000000000000000000000000000000000000000..0254aeea560b5256430215f65a0107cdd87f7d40 --- /dev/null +++ b/favorite/models.py @@ -0,0 +1,29 @@ +from django.db import models +from posts.models import Post as post +from users.models import UserInfo + + +# Create your models here. +class UserFavoriteFolder(models.Model): + user = models.ForeignKey(UserInfo, + related_name='favorite_folder_set', + null=False, + on_delete=models.CASCADE, + verbose_name='所关联的用户') + + folder_name = models.CharField(max_length=20, null=True, verbose_name='收藏文件夹名字') + create_at = models.DateTimeField(auto_now_add=True) + + +class UserFavorite(models.Model): + folder = models.ForeignKey(UserFavoriteFolder, + related_name='favorite_set', + null=False, + on_delete=models.CASCADE, + verbose_name='所关联的文件夹') + + post = models.ForeignKey(post, + related_name='related_favorite', + null=False, + on_delete=models.CASCADE, + verbose_name='收藏所关联的文章') diff --git a/favorite/serializers.py b/favorite/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..27606cb0276d3097ca159bb26375be66cd11710a --- /dev/null +++ b/favorite/serializers.py @@ -0,0 +1,38 @@ +import os +from collections import OrderedDict +from rest_framework import serializers + +from favorite.models import UserFavorite, UserFavoriteFolder +from posts.models import Post + +ENV_DEFINE = os.getenv('position') +if ENV_DEFINE == 'online': + prefix = 'https://qiusuo-1622447-1309638607.ap-shanghai.run.tcloudbase.com/' +else: + prefix = 'http://127.0.0.1:8000/' + +class UserFavoriteSerializer(serializers.ModelSerializer): + class UserFavoritePostSerializer(serializers.ModelSerializer): + class Meta: + model = Post + fields = ['id', 'title', 'content'] + + def to_representation(self, instance): + res = super().to_representation(instance=instance) + res['url'] = prefix +'post-manage/posts/' + str(res['id']) + '/' + del res['id'] + return res + + post = UserFavoritePostSerializer(many=False) + + class Meta: + model = UserFavorite + fields = ['post'] + + +class UserFavoriteFoldSerializer(serializers.ModelSerializer): + favorite_set = UserFavoriteSerializer(many=True, read_only=True) + + class Meta: + model = UserFavoriteFolder + fields = ['folder_name', 'favorite_set'] diff --git a/favorite/tests.py b/favorite/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/favorite/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/favorite/urls.py b/favorite/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..082a2e39161f4ef4ba0b99c778ecd6d474950024 --- /dev/null +++ b/favorite/urls.py @@ -0,0 +1,17 @@ +from django.urls import path, include +from rest_framework import routers + +from favorite.views import favorite_api + +favorite_api_router = routers.DefaultRouter() + +favorite_api_router.register('favoritesfolder', + favorite_api.UserFavoriteFolderViewSet, + basename='userFavoritefolder') + +favorite_api_router.register('favorites', + favorite_api.UserFavoriteViewSet, + basename='userFavorite') +urlpatterns = [ + path('', include((favorite_api_router.urls, 'favorite'), namespace='favorites')) +] diff --git a/favorite/views/__init__.py b/favorite/views/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/favorite/views/favorite_api.py b/favorite/views/favorite_api.py new file mode 100644 index 0000000000000000000000000000000000000000..e0d396e48a7fc439158a4f06b147bf1f3eefb220 --- /dev/null +++ b/favorite/views/favorite_api.py @@ -0,0 +1,16 @@ +from django_filters import OrderingFilter +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.viewsets import ModelViewSet + +from favorite.models import UserFavorite, UserFavoriteFolder +from favorite.serializers import UserFavoriteSerializer, UserFavoriteFoldSerializer + + +class UserFavoriteFolderViewSet(ModelViewSet): + queryset = UserFavoriteFolder.objects.all().order_by('create_at') + serializer_class = UserFavoriteFoldSerializer + + +class UserFavoriteViewSet(ModelViewSet): + queryset = UserFavorite.objects.all() + serializer_class = UserFavoriteSerializer \ No newline at end of file diff --git a/identifier.sqlite b/identifier.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/index.py b/index.py index da3ec1d1ebb4bc0979e1f0c0e44cf37919738308..3fb9df8512ccdff8fc652e5f154496b868b8b5dc 100644 --- a/index.py +++ b/index.py @@ -1,3 +1,5 @@ +import os + from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response @@ -5,7 +7,14 @@ from rest_framework.reverse import reverse from users.urls import user_api_router -prefix = 'https://qiusuo-1622447-1309638607.ap-shanghai.run.tcloudbase.com/' +# + + +ENV_DEFINE = os.getenv('position') +if ENV_DEFINE == 'online': + prefix = 'https://qiusuo-1622447-1309638607.ap-shanghai.run.tcloudbase.com/' +else: + prefix = 'http://127.0.0.1:8000/' @api_view(['GET']) @@ -14,8 +23,9 @@ def root(request): return Response({ 'swagger-doc': prefix + 'doc/', 'docs': prefix + 'docs/', - 'operation-manage': prefix + 'operation-manage/', - 'user-manage': prefix + 'user-manage/', - 'post-manage': prefix + 'post-manage/', - 'comment-manage': prefix + 'comment-manage/', + # 'operation-manage': prefix + 'operation-manage/', + # 'user-manage': prefix + 'user-manage/', + # 'post-manage': prefix + 'post-manage/', + # 'comment-manage': prefix + 'comment-manage/', + # 'subject-manage': prefix + 'subject-manage/', }) diff --git a/operations/migrations/0001_initial.py b/operations/migrations/0001_initial.py index 2ddc7629f80cf299cd8f16cf0783d21ed8ca3bc5..84da114ed391eab75e599255d3ac1597e9b9eec2 100644 --- a/operations/migrations/0001_initial.py +++ b/operations/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.6 on 2022-03-05 10:04 +# Generated by Django 3.2.6 on 2022-03-22 05:27 from django.db import migrations, models diff --git a/operations/serializers.py b/operations/serializers.py index bc9f0742a45377289984364e25e1667a2286bf90..a2a492271ffa5ce4862f124cf6dc03985cc4bba1 100644 --- a/operations/serializers.py +++ b/operations/serializers.py @@ -1,10 +1,14 @@ +import re from datetime import datetime, timedelta +from django.contrib.auth.models import User from django.utils import timezone from rest_framework import serializers +from rest_framework.authtoken.models import Token from rest_framework.exceptions import ValidationError from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler +from backend import constants from operations.models import VerifyCode from users.models import UserInfo @@ -28,7 +32,10 @@ class RegisterSerializer(serializers.ModelSerializer): max_length=12, min_length=6, write_only=True, - label='重置密码') + error_messages={ + "blank": "密码不能为空", + "required": "请再次输入密码", + }, label='重置密码') def validate_code(self, code): """验证码校验""" @@ -52,6 +59,7 @@ class RegisterSerializer(serializers.ModelSerializer): def validate(self, attrs): password = attrs['password'] password2 = attrs['password2'] + if password2 != password: raise ValidationError('两次密码不一致,请重新输入!') return attrs @@ -62,11 +70,6 @@ class RegisterSerializer(serializers.ModelSerializer): user = super().create(validated_data) user.set_password(validated_data['password']) user.save() - - # 补充生成记录登录状态的token - payload = jwt_payload_handler(user) - token = jwt_encode_handler(payload) - user.token = token # TODO: we have some problems here. return user class Meta: @@ -94,3 +97,54 @@ class SmsVerifyCodeSerializer(serializers.Serializer): phone=phone).count(): raise ValidationError("距离上一次发送未超过60s") return phone + + +class LoginSerializer(serializers.ModelSerializer): + username = serializers.CharField( + max_length=150, + required=True, + ) + password = serializers.CharField( + min_length=6, + max_length=128, + required=True + ) + + def validate(self, attrs): + username = attrs.get('username') + password = attrs.get('password') + # 如果是手机号 + if re.match(constants.REGEX_MOBILE, username): + # 以手机号登录 + user = UserInfo.objects.filter(phone=username).first() + elif re.match(constants.REGEX_EMAIL, username): + # 以邮箱登录 + user = UserInfo.objects.filter(email=username).first() + else: + # 以用户名登录 + user = UserInfo.objects.filter(username=username).first() + + if not user: + raise ValidationError('用户名不存在') + + if user and user.check_password(password): + # 登录成功,生成token + payload = jwt_payload_handler(user) + token = jwt_encode_handler(payload) + self.context['token'] = token + self.context['username'] = user.username + self.context['user'] = user + return attrs + else: + raise ValidationError('用户名或密码错误') + + # def create(self, validated_data): + # obj, created = Token.objects.update_or_create( + # key=self.context['token'], + # user=self.context['user'] + # ) + # return obj + + class Meta: + model = UserInfo + fields = ['username', 'password'] diff --git a/operations/urls.py b/operations/urls.py index 5d8731384b9a59a58f1d9acb91f52d9af97036f6..12c8b022c51fabb7b9ff69a76542d1905fe85be2 100644 --- a/operations/urls.py +++ b/operations/urls.py @@ -1,7 +1,8 @@ from django.urls import path, include from rest_framework import routers -from rest_framework_jwt.views import obtain_jwt_token, JSONWebTokenAPIView +from rest_framework_jwt.views import obtain_jwt_token, JSONWebTokenAPIView, ObtainJSONWebToken +from operations.views.user_operations.login import LoginViewSet from operations.views.user_operations.register import RegisterViewSet, SendSmsVerifyCodeViewSet operations_api_router = routers.DefaultRouter() @@ -9,8 +10,11 @@ operations_api_router = routers.DefaultRouter() operations_api_router.register('register', RegisterViewSet, basename='register') operations_api_router.register('verify-code', SendSmsVerifyCodeViewSet, - basename="verify-code") + basename='verify-code') +operations_api_router.register('login', LoginViewSet, + basename='login') + urlpatterns = [ path('', include((operations_api_router.urls, 'operations'), namespace='operations')), - path('login/', obtain_jwt_token, name='login'), + # path('login/', obtain_jwt_token, name='login'), ] diff --git a/operations/views/user_operations/login.py b/operations/views/user_operations/login.py new file mode 100644 index 0000000000000000000000000000000000000000..fe0486586f169fd58a3cda5d30c5264c8bc91e64 --- /dev/null +++ b/operations/views/user_operations/login.py @@ -0,0 +1,28 @@ +from django.db.models import Q +from rest_framework import status +from rest_framework.authtoken.models import Token +from rest_framework.mixins import CreateModelMixin +from rest_framework.response import Response +from rest_framework.viewsets import GenericViewSet +from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler +from operations.serializers import LoginSerializer +from users.models import UserInfo + + +class LoginViewSet(GenericViewSet, CreateModelMixin): + serializer_class = LoginSerializer + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + # self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + + res = serializer.context + return Response(data={ + "code": 201, + "msg": 'success', + "username": res.get('username'), + "token": res.get('token') + }, status=status.HTTP_201_CREATED, headers=headers) + diff --git a/posts/migrations/0001_initial.py b/posts/migrations/0001_initial.py index 5e93775a168cb152f5ae79d3248ffae5bd61c237..e023b1ba6ca69076bd0151961716d6d68a467321 100644 --- a/posts/migrations/0001_initial.py +++ b/posts/migrations/0001_initial.py @@ -1,6 +1,7 @@ -# Generated by Django 3.2.6 on 2022-03-05 10:04 +# Generated by Django 3.2.6 on 2022-03-22 05:27 from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -8,6 +9,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('users', '0002_alter_userinfo_phone'), ] operations = [ @@ -16,13 +18,15 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('title', models.TextField(verbose_name='标题')), + ('title', models.TextField(unique=True, verbose_name='标题')), ('content', models.TextField(blank=True, verbose_name='内容')), ('excerpt', models.TextField(blank=True, null=True, verbose_name='引用')), ('category', models.CharField(blank=True, max_length=20, null=True, verbose_name='种类')), ('status', models.CharField(default='active', max_length=20, verbose_name='状态')), ('parent', models.BigIntegerField(default=-1, verbose_name='父节点')), ('comment_count', models.BigIntegerField(default=0, verbose_name='评论数')), + ('likes', models.IntegerField(default=0, verbose_name='点赞数')), + ('author', models.ForeignKey(blank=True, db_constraint=False, default=None, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='post_set', to='users.userinfo', verbose_name='作者')), ], ), ] diff --git a/posts/models.py b/posts/models.py index 2b567fc72e7be9c0ab83f22190445c1136798d4c..4e25e57d84243bfa73d7414f486947ed7f8eb47c 100644 --- a/posts/models.py +++ b/posts/models.py @@ -15,7 +15,7 @@ class Post(models.Model): verbose_name=u'作者' ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=u'创建时间') - title = models.TextField(verbose_name=u'标题') + title = models.TextField(unique=True, verbose_name=u'标题') content = models.TextField(blank=True, verbose_name=u'内容') excerpt = models.TextField(blank=True, verbose_name=u'引用', null=True) category = models.CharField(blank=True, max_length=20, verbose_name=u'种类', null=True) @@ -26,13 +26,11 @@ class Post(models.Model): ) parent = models.BigIntegerField(default=-1, verbose_name=u'父节点') comment_count = models.BigIntegerField(default=0, verbose_name=u'评论数') + likes = models.IntegerField(default=0, verbose_name='点赞数') - # owner = models.ManyToManyField( - # User, - # related_name='post_owner_set', - # blank=True, - # db_constraint=False - # ) def __str__(self): return self.author.id + + def __repr__(self): + return self.author.id diff --git a/posts/serializers.py b/posts/serializers.py index af7c44ce51052fa04371cd9fa4d0f1dbf0967f00..4a367bcf292a711254840ce37fb19ba2e9de2197 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -1,4 +1,7 @@ +from collections import OrderedDict + from rest_framework import serializers +from rest_framework.exceptions import ValidationError from posts.models import Post from users.models import UserInfo @@ -8,11 +11,22 @@ class AuthorSerializer(serializers.ModelSerializer): class Meta: model = UserInfo fields = ['id', 'username'] + depth = 1 class PostSerializer(serializers.ModelSerializer): - author = AuthorSerializer() + author = serializers.SlugRelatedField( + queryset=UserInfo.objects.all(), slug_field='id') class Meta: model = Post + depth = 1 fields = '__all__' + + def to_representation(self, instance): + res = super().to_representation(instance=instance) + repr_author = OrderedDict() + repr_author['id'] = instance.author.id + repr_author['username'] = instance.author.username + res['author'] = repr_author + return res diff --git a/posts/views/post_api.py b/posts/views/post_api.py index 88de5fa4362a162ad00f0b73827e621a6e5846bf..dc278125396e6f285821b26a831441d041949db4 100644 --- a/posts/views/post_api.py +++ b/posts/views/post_api.py @@ -1,3 +1,8 @@ +from django.core.exceptions import ObjectDoesNotExist +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.filters import OrderingFilter from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet @@ -7,7 +12,25 @@ from posts.serializers import PostSerializer class PostViewSet(ModelViewSet): - queryset = Post.objects.all() + # 最新的帖子 + queryset = Post.objects.all().order_by('created_at').reverse() serializer_class = PostSerializer + filter_class = PostFilter + filter_backends = [DjangoFilterBackend, OrderingFilter] + + @action(methods=['POST'], detail=True) + def thumbs_up(self, request, pk): + instance = Post.objects.get(id=pk) + if not instance: + raise ObjectDoesNotExist() + + instance.likes += 1 + instance.save() + + return Response(data={ + "code": 200, + "msg": "success", + "likes": instance.likes, + }, status=status.HTTP_200_OK) diff --git a/subjects/__init__.py b/subjects/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subjects/admin.py b/subjects/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/subjects/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/subjects/apps.py b/subjects/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..c3bdd6834d7113895ca84a42799737668e38d660 --- /dev/null +++ b/subjects/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class SubjectsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'subjects' diff --git a/subjects/migrations/0001_initial.py b/subjects/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..b9fb10ac5f48e4bdfa967afe39a5bf5ba5f19d41 --- /dev/null +++ b/subjects/migrations/0001_initial.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.6 on 2022-03-15 08:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Subject', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + ] diff --git a/subjects/migrations/0002_auto_20220318_1416.py b/subjects/migrations/0002_auto_20220318_1416.py new file mode 100644 index 0000000000000000000000000000000000000000..fc751ffacb1a38135852119a4fecf047b941450e --- /dev/null +++ b/subjects/migrations/0002_auto_20220318_1416.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.6 on 2022-03-18 06:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('subjects', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='subject', + name='level', + field=models.IntegerField(null=True, verbose_name='层级'), + ), + migrations.AddField( + model_name='subject', + name='subject', + field=models.CharField(max_length=255, null=True, verbose_name='学科名'), + ), + ] diff --git a/subjects/migrations/0003_alter_subject_subject.py b/subjects/migrations/0003_alter_subject_subject.py new file mode 100644 index 0000000000000000000000000000000000000000..033972283149be0fd23c0f4b24a430a2d2e00f47 --- /dev/null +++ b/subjects/migrations/0003_alter_subject_subject.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2022-03-18 07:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('subjects', '0002_auto_20220318_1416'), + ] + + operations = [ + migrations.AlterField( + model_name='subject', + name='subject', + field=models.CharField(max_length=255, null=True, unique=True, verbose_name='学科名'), + ), + ] diff --git a/subjects/migrations/0004_auto_20220318_1539.py b/subjects/migrations/0004_auto_20220318_1539.py new file mode 100644 index 0000000000000000000000000000000000000000..97239a8f4a6f3ebcc6b8903abd8e959592623239 --- /dev/null +++ b/subjects/migrations/0004_auto_20220318_1539.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.6 on 2022-03-18 07:39 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('subjects', '0003_alter_subject_subject'), + ] + + operations = [ + migrations.AddField( + model_name='subject', + name='level2_subjects', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='level2_subject_set', to='subjects.subject', verbose_name='二级学科'), + ), + migrations.AddField( + model_name='subject', + name='level3_subjects', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='level3_subject_set', to='subjects.subject', verbose_name='三级学科'), + ), + ] diff --git a/subjects/migrations/0005_auto_20220321_2100.py b/subjects/migrations/0005_auto_20220321_2100.py new file mode 100644 index 0000000000000000000000000000000000000000..321064f723250ace250724ddb035438b2f432bca --- /dev/null +++ b/subjects/migrations/0005_auto_20220321_2100.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.6 on 2022-03-21 13:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('subjects', '0004_auto_20220318_1539'), + ] + + operations = [ + migrations.CreateModel( + name='SubjectCategory_1', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('subject', models.CharField(max_length=255, null=True, unique=True, verbose_name='一级学科名')), + ], + ), + migrations.CreateModel( + name='SubjectCategory_2', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('subject', models.CharField(max_length=255, null=True, unique=True, verbose_name='二级学科名')), + ], + ), + migrations.CreateModel( + name='SubjectCategory_3', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('subject', models.CharField(max_length=255, null=True, unique=True, verbose_name='三级学科名')), + ], + ), + migrations.DeleteModel( + name='Subject', + ), + ] diff --git a/subjects/migrations/__init__.py b/subjects/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subjects/models.py b/subjects/models.py new file mode 100644 index 0000000000000000000000000000000000000000..caa313577b0b8450db0f86ed8e730ca6545f864a --- /dev/null +++ b/subjects/models.py @@ -0,0 +1,41 @@ +from django.db import models + + +class SubjectCategory_3(models.Model): + subject = models.CharField( + unique=True, + max_length=255, + null=True, + verbose_name='三级学科名') + level = 3 + + def __str__(self): + return self.subject + " " + str(self.level) + + +class SubjectCategory_2(models.Model): + subject = models.CharField( + unique=True, + max_length=255, + null=True, + verbose_name='二级学科名') + + level = 2 + + def __str__(self): + return self.subject + " " + str(self.level) + + +class SubjectCategory_1(models.Model): + subject = models.CharField( + unique=True, + max_length=255, + null=True, + verbose_name='一级学科名') + + level = 1 + + def __str__(self): + return self.subject + " " + str(self.level) + + diff --git a/subjects/serializers.py b/subjects/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..6dba0378ca720221a7fac599f15c71beffc4e9c6 --- /dev/null +++ b/subjects/serializers.py @@ -0,0 +1,24 @@ +from rest_framework import serializers +from rest_framework.fields import SerializerMethodField + +from subjects.models import SubjectCategory_1, SubjectCategory_2, SubjectCategory_3 + + +class SubjectSerializer3(serializers.ModelSerializer): + class Meta: + model = SubjectCategory_3 + fields = '__all__' + + +class SubjectSerializer2(serializers.ModelSerializer): + + class Meta: + model = SubjectCategory_2 + fields = '__all__' + + +class SubjectSerializer(serializers.ModelSerializer): + + class Meta: + model = SubjectCategory_1 + fields = '__all__' diff --git a/subjects/urls.py b/subjects/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..c6b1ae9df6cd44630dd06f709e5ccdabd371c7b1 --- /dev/null +++ b/subjects/urls.py @@ -0,0 +1,16 @@ +from django.urls import path, include + +from rest_framework import routers + +from subjects import views + +subject_api_router = routers.DefaultRouter() + +subject_api_router.register('subjects', views.SubjectViewSet, + basename='subjects') + +urlpatterns = [ + path('', include((subject_api_router.urls, 'subjects'), + namespace='subjects')), + +] diff --git a/subjects/views.py b/subjects/views.py new file mode 100644 index 0000000000000000000000000000000000000000..1b72f00fc6a5bea1847745c4d03bf606b5e18d24 --- /dev/null +++ b/subjects/views.py @@ -0,0 +1,13 @@ +from rest_framework.decorators import permission_classes +from rest_framework.mixins import ListModelMixin, CreateModelMixin +from rest_framework.permissions import IsAdminUser +from rest_framework.viewsets import GenericViewSet + +from subjects.models import SubjectCategory_1 +from subjects.serializers import SubjectSerializer + + +@permission_classes([IsAdminUser]) +class SubjectViewSet(GenericViewSet, ListModelMixin, CreateModelMixin): + queryset = SubjectCategory_1.objects.all() + serializer_class = SubjectSerializer diff --git a/users/authentication.py b/users/authentication.py index cd01c52b004b39562475d3f8b475aa3d5f559246..43211fe74d5a393be005196032df9c73141e47d6 100644 --- a/users/authentication.py +++ b/users/authentication.py @@ -1,6 +1,10 @@ +import jwt from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User from django.db.models import Q +from rest_framework.exceptions import AuthenticationFailed +from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication +from rest_framework_jwt.utils import jwt_decode_handler from users.models import UserInfo @@ -9,6 +13,7 @@ class EmailOrPhoneBackend(ModelBackend): """ 自定义用户认证,可使用手机号或邮箱登录 """ + def authenticate(self, request, username=None, password=None, **kwargs): """ :param request: @@ -23,3 +28,21 @@ class EmailOrPhoneBackend(ModelBackend): return user except Exception: return None + + +class JWTAuthentication(BaseJSONWebTokenAuthentication): + def authenticate(self, request): + token = request.META.get('HTTP_Authorization'.upper()) + try: + payload = jwt_decode_handler(token) + except jwt.ExpiredSignature: + raise AuthenticationFailed('过期了') + except jwt.DecodeError: + raise AuthenticationFailed('解码错误') + except jwt.InvalidTokenError: + raise AuthenticationFailed('不合法的token') + + print(payload) + + user = payload + return user, token diff --git a/users/migrations/0002_alter_userinfo_phone.py b/users/migrations/0002_alter_userinfo_phone.py new file mode 100644 index 0000000000000000000000000000000000000000..0bf653941e1d7d0bc2f438cb36af157e4dd97241 --- /dev/null +++ b/users/migrations/0002_alter_userinfo_phone.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2022-03-05 13:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='userinfo', + name='phone', + field=models.CharField(default=None, max_length=11, null=True, verbose_name='手机号码'), + ), + ] diff --git a/posts/migrations/0002_post_author.py b/users/migrations/0002_alter_usertitle_owner.py similarity index 34% rename from posts/migrations/0002_post_author.py rename to users/migrations/0002_alter_usertitle_owner.py index 4c2b4751f48e36e678fb2126ff8c9be93b288559..f7955ee20d92c191b0d6c32e819b42928c9bc389 100644 --- a/posts/migrations/0002_post_author.py +++ b/users/migrations/0002_alter_usertitle_owner.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.6 on 2022-03-05 10:04 +# Generated by Django 3.2.6 on 2022-03-06 05:52 from django.db import migrations, models import django.db.models.deletion @@ -6,17 +6,14 @@ import django.db.models.deletion class Migration(migrations.Migration): - initial = True - dependencies = [ - ('posts', '0001_initial'), ('users', '0001_initial'), ] operations = [ - migrations.AddField( - model_name='post', - name='author', - field=models.ForeignKey(blank=True, db_constraint=False, default=None, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='post_set', to='users.userinfo', verbose_name='作者'), + migrations.AlterField( + model_name='usertitle', + name='owner', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='Title_set', to='users.userinfo', verbose_name='用户头衔'), ), ] diff --git a/comments/migrations/0002_comment_author.py b/users/migrations/0003_alter_usertitle_owner.py similarity index 41% rename from comments/migrations/0002_comment_author.py rename to users/migrations/0003_alter_usertitle_owner.py index fdd292c3412b37a437d0efcccca3e0f578e395c7..1ca6bde209fa3d59ac5a63b7fe48cf253296466b 100644 --- a/comments/migrations/0002_comment_author.py +++ b/users/migrations/0003_alter_usertitle_owner.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.6 on 2022-03-05 10:04 +# Generated by Django 3.2.6 on 2022-03-06 05:57 from django.db import migrations, models import django.db.models.deletion @@ -6,17 +6,14 @@ import django.db.models.deletion class Migration(migrations.Migration): - initial = True - dependencies = [ - ('comments', '0001_initial'), - ('users', '0001_initial'), + ('users', '0002_alter_usertitle_owner'), ] operations = [ - migrations.AddField( - model_name='comment', - name='author', - field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='comment_set', to='users.userinfo', verbose_name='评论者'), + migrations.AlterField( + model_name='usertitle', + name='owner', + field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, related_name='title_set', to='users.userinfo', verbose_name='用户头衔'), ), ] diff --git a/users/migrations/0004_alter_usertitle_owner.py b/users/migrations/0004_alter_usertitle_owner.py new file mode 100644 index 0000000000000000000000000000000000000000..0d5bc668808c5a7ee8ba99c13b97f3a37b284441 --- /dev/null +++ b/users/migrations/0004_alter_usertitle_owner.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.6 on 2022-03-06 06:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0003_alter_usertitle_owner'), + ] + + operations = [ + migrations.AlterField( + model_name='usertitle', + name='owner', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='title_set', to='users.userinfo', verbose_name='用户头衔'), + ), + ] diff --git a/users/migrations/0005_merge_20220323_1055.py b/users/migrations/0005_merge_20220323_1055.py new file mode 100644 index 0000000000000000000000000000000000000000..f5df7156061ce9dc966685f8e05ad14647174715 --- /dev/null +++ b/users/migrations/0005_merge_20220323_1055.py @@ -0,0 +1,14 @@ +# Generated by Django 3.2.6 on 2022-03-23 02:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0002_alter_userinfo_phone'), + ('users', '0004_alter_usertitle_owner'), + ] + + operations = [ + ] diff --git a/users/migrations/0006_alter_usertitle_owner.py b/users/migrations/0006_alter_usertitle_owner.py new file mode 100644 index 0000000000000000000000000000000000000000..78a9f44c901910c1e5dd09a9b0584737d5f67bd2 --- /dev/null +++ b/users/migrations/0006_alter_usertitle_owner.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.6 on 2022-03-23 02:55 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0005_merge_20220323_1055'), + ] + + operations = [ + migrations.AlterField( + model_name='usertitle', + name='owner', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='title_set', to='users.userinfo', verbose_name='头衔拥有者'), + ), + ] diff --git a/users/models.py b/users/models.py index 162e7fc6157071cafa3b05dafbcffa0a8e6ffef2..882783fcfccee01714b025d95a229668a68bdc44 100644 --- a/users/models.py +++ b/users/models.py @@ -20,7 +20,7 @@ class UserInfo(User): phone = models.CharField( max_length=11, null=True, - blank=True, + blank=False, default=None, verbose_name=u'手机号码', ) # 电话 @@ -62,3 +62,4 @@ class UserTitle(models.Model): verbose_name=u'头衔拥有者' ) title_str = models.TextField(blank=False, verbose_name='用户头衔') + diff --git a/users/serializers.py b/users/serializers.py index 60bbe5b7e0a3d7a2b57812bafa8ebfa196c06513..e015159f9a7c8016f926f577a122d07bf93c92e0 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -12,7 +12,7 @@ class InnerPostSetSerializer(serializers.ModelSerializer): class UserProfileSerializer(serializers.ModelSerializer): - post_set = InnerPostSetSerializer(many=True) + # post_set = InnerPostSetSerializer(many=True) class Meta: model = UserInfo diff --git a/users/tests.py b/users/tests.py index 7ce503c2dd97ba78597f6ff6e4393132753573f6..7da7ef2ba10c7c65edf00140b551b9a0e76a7cde 100644 --- a/users/tests.py +++ b/users/tests.py @@ -1,3 +1,86 @@ -from django.test import TestCase +import re # 正则 +from collections import OrderedDict # 创建有序字典 +from django.conf import settings # 导入setting配置文件(获取项目根路径urls.py) +from django.utils.module_loading import import_string # 字符串导入模块 +from django.urls import URLPattern, URLResolver # Django自定义的类,可以判断当前URL是否为根路径(URLPattern)或继续向下分发(URLResolver ) + + +def check_url_exclude(url): + """ + 排除一些特定的URL + :param url: 待检验的URL + :return: + """ + for regex in settings.AUTO_DISCOVER_EXCLUDE: # 将要定向排除的URL(可包含正则) 按照列表的形式写入配置文件 settings.AUTO_DISCOVER_EXCLUDE 下 + if re.match(regex, url): + return True + + +""" +示例: +AUTO_DISCOVER_EXCLUDE = [ + '/admin/.*', + '/login/', + '/logout/', + '/index/', +] +""" + + +def recursion_urls(pre_namespace, pre_url, urlpatterns, url_ordered_dict): + """ + 递归的去获取URL + :param pre_namespace: namespace前缀,用于拼接name + :param pre_url: url前缀,用于拼接url + :param urlpatterns: 路由关系列表 + :param url_ordered_dict: 用于保存递归中获取的所有路由 + :return: + """ + for item in urlpatterns: + if isinstance(item, URLPattern): # 已经是根网址,获取name及url写入url_ordered_dict + if not item.name: # 没有别名(name)的路由地址直接跳过 + continue + # 拼接路由别名(包含分发下来的namespace;如 "rbac:menu_list") + if pre_namespace: + name = "%s:%s" % (pre_namespace, item.name) + else: + name = item.name + # 拼接路由地址URl(包含分发下来的上层路由;如 "/rbac/menu/list") + url = pre_url + item.pattern.regex.pattern # 此时拼接的路由包含起止符号,如:/^rbac/^menu/list/$ + url = url.replace("^", "").replace("$", "") # 删除起止符:/rbac/menu/list/ + # 排除一些特定的路由URL + if check_url_exclude(url): # 调用check_url_exclude函数定向排除部分URL + continue + url_ordered_dict[name] = {"name": name, "url": url} + + elif isinstance(item, URLResolver): # 路由分发,递归操作 + if pre_namespace: # 上次循环(上一层)分发是否包含namespace + if item.namespace: # 本次循环(当前层)是否包含namespace + namespace = "%s:%s" % (pre_namespace, item.namespace,) # 上层、当前层都包含直接拼接两层的namespace + else: + namespace = pre_namespace # 当前层分发不包含namespace,直接用上一层的 + else: + if item.namespace: + namespace = item.namespace # 上一层分发不包含namespace,直接使用当前层的 + else: + namespace = None # 上一层、当前层都没有,直接定义层none + recursion_urls(namespace, pre_url + item.pattern.regex.pattern, item.url_patterns, + url_ordered_dict) # 递归继续执行 + + +def get_all_url_dict(): + """ + 获取项目所有路由 + :return: + """ + url_ordered_dict = OrderedDict() # 包含本项目所有权限URl的有序字典 + md = import_string(settings.ROOT_URLCONF) # 配置文件内的 ROOT_URLCONF 为本项目根路由urls.py 的路径(字符串),使用 import_string 用字符串加载模块 + recursion_urls(None, "/", md.urlpatterns, + url_ordered_dict) # 调用 recursion_urls 函数获取所有路由字典,根路径下没有namespace 定义为 None;没有url前缀 定义为 / + return url_ordered_dict + + +if __name__ == '__main__': + + dict = get_all_url_dict() -# Create your tests here. diff --git a/users/urls.py b/users/urls.py index 7880a11f565fc77cc957ca545d6e5a670b8821a7..86514507f32b0622fc0eb4c8d148234049231cca 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,12 +1,16 @@ from django.urls import path, include from rest_framework import routers -from users.views import user_api +from users.views import user_api, token_api user_api_router = routers.DefaultRouter() -user_api_router.register('users', user_api.UserInfoViewSet, basename='users') -user_api_router.register('titles', user_api.UserTitleViewSet, basename='titles') +user_api_router.register('users', user_api.UserInfoViewSet, + basename='users') +user_api_router.register('titles', user_api.UserTitleViewSet, + basename='titles') +user_api_router.register('tokens', token_api.TokenViewSet, + basename='tokens') urlpatterns = [ path('', include((user_api_router.urls, 'users'), namespace='users')) diff --git a/users/views/token_api.py b/users/views/token_api.py new file mode 100644 index 0000000000000000000000000000000000000000..531d41a3b95580a5322df8e12f752532e10e3957 --- /dev/null +++ b/users/views/token_api.py @@ -0,0 +1,10 @@ +from rest_framework.authtoken.models import Token +from rest_framework.viewsets import GenericViewSet +from rest_framework.mixins import ListModelMixin + +from users.serializers import UserTokenSerializer + + +class TokenViewSet(GenericViewSet, ListModelMixin): + queryset = Token.objects.all() + serializer_class = UserTokenSerializer diff --git a/users/views/user_api.py b/users/views/user_api.py index 441410d6f550fd44211719997ebf08d49880d9ab..a4f121e8571c2ec30a7ebb24bd9cf1e13b02ab89 100644 --- a/users/views/user_api.py +++ b/users/views/user_api.py @@ -3,7 +3,7 @@ from rest_framework.filters import OrderingFilter from rest_framework.viewsets import ModelViewSet from users.models import UserInfo, UserTitle -from users.serializers import UserProfileSerializer, UserTitleSerializer +from users.serializers import UserProfileSerializer class UserInfoViewSet(ModelViewSet):