class ListAuctionItems(ListAPIView): """ API to list all items on auction, upcoming auctions, previous auctions. """ serializer_class = AuctionItemSerializer schema = AutoSchema(manual_fields=[ coreapi.Field( name="page", required=False, location="query", schema=coreschema.Integer(description="Page to be retrieved. Defaults to 0"), ), coreapi.Field( "items_per_page", required=False, location="query", schema=coreschema.Integer(description="Number of items to be displayed per page. Defaults to 10") ), coreapi.Field( "upcoming", required=False, location="query", schema=coreschema.Boolean( description="Display only upcoming Auction Items. Takes boolean. Defaults to false") ), coreapi.Field( "previous", required=False, location="query", schema=coreschema.Boolean( description="Display only previous Auction Items. Takes boolean. Defaults to false") ), ]) def get_queryset(self): queryset = AuctionItem.objects.all() page = self.request.query_params.get('page', 0) items_per_page = self.request.query_params.get('items_per_page', 10) upcoming = self.request.query_params.get('upcoming', False) previous = self.request.query_params.get('previous', False) if upcoming: queryset = queryset.filter(start_time__gt=timezone.now()) if previous: queryset = queryset.filter(end_time__lt=timezone.now()) queryset = queryset[page * items_per_page: (page + 1) * items_per_page] return queryset
def get_schema_fields(self, view): super().get_schema_fields(view) choices = { STATES.DRAFT: 'Incomplete metadata', STATES.SUBMITTED: 'Pending validation', STATES.REFUSED: 'Refused', STATES.ACCEPTED: 'Validated', } return [ coreapi.Field( name='status', required=False, location='query', schema=coreschema.Boolean( title="Campaign status", description="0 for closed campaign, 1 for ongoing campaign" ), ), coreapi.Field( name='picture_status', required=False, location='query', schema=coreschema.Enum( choices, description=str(pformat(choices)), title="Picture status", ) ) ]
class CheckAlphabet(APIView): ''' simple APIView for alphacheck api ''' schema = AutoSchema(manual_fields=[ coreapi.Field( 'case_insensitive', required=False, location='query', schema=coreschema.Boolean(), ), ]) # pylint: disable=redefined-builtin, unused-argument def get(self, request, query, format=None): ''' HTTP GET method that checks whether query contains all letters of the alphabet ''' try: case_insensitive = json.loads( request.query_params.get('case_insensitive', 'false')) except json.JSONDecodeError: case_insensitive = False try: response = has_all_alphabet(query, case_insensitive=case_insensitive, version=request.version) except KeyError: raise Http404 return Response({ 'response': response, })
def _annotated_type_to_coreschema( annotated_type: type) -> coreschema.schemas.Schema: if annotated_type is bool or issubclass(annotated_type, schema.Boolean): return coreschema.Boolean() elif annotated_type is int or issubclass(annotated_type, schema.Integer): return coreschema.Integer() elif annotated_type is float or issubclass(annotated_type, schema.Number): return coreschema.Number() return coreschema.String()
def get_schema_fields(self, view): return [ coreapi.Field( name='is_paid', required=False, schema=coreschema.Boolean( description='If true, returns only payments already paid'), location='query', ) ]
def get_serializer_fields(self, path, method): fields = [] if method == 'POST': fields = [ coreapi.Field(name="group_id", required=True, location="form", schema=coreschema.Integer( title="group", description="Id группы"), description='group_id'), coreapi.Field( name="title", required=True, location="form", schema=coreschema.String(title="title", description="Название задачи"), description='Название задачи', ), coreapi.Field( name="user", required=True, location="form", schema=coreschema.Array(title="user", description="Юзеры в задаче"), description='Юзеры в задаче', ), ] if method == 'PUT': fields = [ coreapi.Field(name="done", required=True, location="form", schema=coreschema.Boolean( title="done", description="Завершено"), description='Завершено'), coreapi.Field( name="title", required=True, location="form", schema=coreschema.String(title="title", description="Название задачи"), description='Название задачи', ), coreapi.Field( name="user", required=True, location="form", schema=coreschema.Array(title="user", description="Юзеры в задаче"), description='Юзеры в задаче', ), ] return fields
def get_param_schema(annotated_type: typing.Type) -> coreschema.schemas.Schema: if issubclass(annotated_type, (bool, typesystem.Boolean)): return coreschema.Boolean() elif issubclass(annotated_type, int): return coreschema.Integer() elif issubclass(annotated_type, float): return coreschema.Number() elif issubclass(annotated_type, typesystem.Enum): enum = typing.cast(typing.Type[typesystem.Enum], annotated_type) return coreschema.Enum(enum=enum.enum) return coreschema.String()
def get_serializer_fields(self, path, method): extra_fields = [] if method == 'PUT': extra_fields = [ coreapi.Field(name='password', required=False, location='form', schema=coreschema.String( description="New password", title='password', )), coreapi.Field( name='position', required=False, location='form', type=coreschema.Integer, schema=coreschema.Integer( description= "Device outlet position. Mandatory for changing outlet name, remove user or outlet status. Must be an integer. Only admins can remove user from outlet", title="position")), coreapi.Field( name='name', required=False, location='form', schema=coreschema.String( description= "Change the outlet name in the position provided", title='name')), coreapi.Field( name='status', required=False, location='form', type=coreschema.String, schema=coreschema.String( description= "Turn on or off the remote server connected to the outlet position provided. Values accepted: \'on\' or \'off\'", title='status')), coreapi.Field( name='rempos', required=False, location='form', schema=coreschema.Boolean( description= "Remove user from position. Only for admins. Values accepted: True or False (False by default)", title='rempos', )), ] manual_fields = super(CustomUserRestSchema, self).get_serializer_fields(path, method) return manual_fields + extra_fields
class EncounterListView(generics.ListAPIView): """ List own encounters """ pagination_class = LimitOffsetPagination serializer_class = EncounterSerializer def get_queryset(self): queryset = Encounter.objects.filter(accounts__user=self.request.user).order_by('-started_at') since = self.request.query_params.get('since', None) if since is not None: queryset = queryset.filter(started_at__gte=since) area_id = self.request.query_params.get('area_id', None) if area_id is not None: queryset = queryset.filter(area_id=int(area_id)) success = self.request.query_params.get('success', None) if success is not None: queryset = queryset.filter(success=strtobool(success)) return queryset schema = AutoSchema(manual_fields=[ coreapi.Field( name="since", required=False, location='query', schema=coreschema.Integer( title="Since", description="Earliest time (UNIX timestamp)", ), ), coreapi.Field( name="area_id", required=False, location='query', schema=coreschema.Integer( title="Area ID", description="ID of the area where encounter took place", ), ), coreapi.Field( name="success", required=False, location='query', schema=coreschema.Boolean( title="Success", description="Return only successful or unsuccessful encounters", ), ), ])
def get_param_schema(annotated_type: typing.Type) -> coreschema.schemas.Schema: schema_kwargs = {'description': getattr(annotated_type, 'description', '')} if issubclass(annotated_type, (bool, typesystem.Boolean)): return coreschema.Boolean(**schema_kwargs) elif issubclass(annotated_type, int): return coreschema.Integer(**schema_kwargs) elif issubclass(annotated_type, float): return coreschema.Number(**schema_kwargs) elif issubclass(annotated_type, typesystem.Enum): enum = typing.cast(typing.Type[typesystem.Enum], annotated_type) return coreschema.Enum(enum=enum.enum, **schema_kwargs) return coreschema.String(**schema_kwargs)
def _annotated_type_to_coreschema( annotated_type: type) -> coreschema.schemas.Schema: if annotated_type is bool or issubclass(annotated_type, schema.Boolean): return coreschema.Boolean() elif annotated_type is int or issubclass(annotated_type, schema.Integer): return coreschema.Integer() elif annotated_type is float or issubclass(annotated_type, schema.Number): return coreschema.Number() elif issubclass(annotated_type, schema.Enum): enum = cast(Type[schema.Enum], annotated_type) return coreschema.Enum(enum=enum.enum) return coreschema.String()
def get_schema(self): if self.dtype == str: return coreschema.String(description=self.description) elif self.dtype == bool: return coreschema.Boolean(description=self.description) elif self.dtype == int: return coreschema.Integer(description=self.description) elif self.dtype == float: return coreschema.Number(description=self.description) elif self.dtype == list: return coreschema.Array(description=self.description) else: raise TypeError("Parameter must have type.")
def field_to_schema(self, field): import coreschema import django.forms as django_forms from django.utils.encoding import force_str title = force_str(field.label) if field.label else '' description = force_str(field.help_text) if field.help_text else '' schema = None if isinstance(field, django_forms.MultipleChoiceField): schema = coreschema.Array( items=coreschema.Enum(enum=list(field.choices)), title=title, description=description) elif isinstance(field, django_forms.ChoiceField) and not isinstance( field, django_forms.ModelChoiceField): choices = list( map( lambda choice: choice[0] if isinstance(choice, tuple) else choice, field.choices)) choices.remove('') schema = coreschema.Enum(enum=choices, title=title, description=description) elif isinstance(field, django_forms.BooleanField): schema = coreschema.Boolean(title=title, description=description) elif isinstance(field, (django_forms.DecimalField, django_forms.FloatField)): schema = coreschema.Number(title=title, description=description) elif isinstance(field, django_forms.IntegerField): schema = coreschema.Integer(title=title, description=description) elif isinstance(field, django_forms.DateField): schema = coreschema.String(title=title, description=description, format='date') elif isinstance(field, django_forms.DateTimeField): schema = coreschema.String(title=title, description=description, format='date-time') elif isinstance(field, django_forms.JSONField): schema = coreschema.Object(title=title, description=description) return schema or coreschema.String(title=title, description=description)
def get_serializer_fields(self, path, method): fields = [] if method in ['PUT', 'POST']: fields = [ coreapi.Field( name="title", required=True, location="form", schema=coreschema.String(title="title", description="title"), description='Название группы', ), coreapi.Field( name="description", required=True, location="form", schema=coreschema.String(title="description", description="description"), description='Описание', ), coreapi.Field( name="category", required=True, location="form", schema=coreschema.Integer(title="category", description="category"), description='ID категории', ), coreapi.Field( name="is_public", required=True, location="form", schema=coreschema.Boolean(title="is_public", description="is_public"), description='Группа публичная', ), ] return fields
def get_schema_fields(self, view): super().get_schema_fields(view) return [ coreapi.Field( name="state", required=False, location="query", schema=coreschema.Boolean( title="Campaign state", description="'draft', 'started' or 'closed'", ), ), coreapi.Field( name="picture__state", required=False, location="query", schema=coreschema.Enum( Picture.STATES, description=str(pformat(Picture.STATES)), title="Picture state", ), ), ]
"nresults", required=False, location="query", schema=coreschema.Integer( description="Maximum number of journeys to return, default " "10. (e.g. 20)"), description="Maximum number of journeys to return, default 10. " "(e.g. 20)", example="20", ), coreapi.Field( "expand_journey", required=False, location="query", schema=coreschema.Boolean( description="Expands the resulting journey into a full " "journey object"), description="Expands the resulting journey into a full " "journey object", ), ], description="Return the timetabled vehicle journeys expected for a given " "stop identified by _stop_id_ from a specific date and time " "identified by _datetime_from_ (optional, default = now). " "\n\n" "All results are paginated based on _nresults_ and a _next_ " "attribute is returned containing the URL to use to retrieve " "more results. The pagination can return up to _nresults_ " "or less if there are no more results for a day, for example.")
def get_schema_fields(self, view): fields = [ coreapi.Field(name=self.category_query_param, required=False, location='query', schema=coreschema.Integer( title='Category', description=force_str( self.category_query_description))), coreapi.Field(name=self.sub_category_query_param, required=False, location='query', schema=coreschema.Integer( title='Sub Category', description=force_str( self.sub_category_query_description))), coreapi.Field(name=self.author_query_param, required=False, location='query', schema=coreschema.Integer( title='Author', description=force_str( self.author_query_description))), coreapi.Field(name=self.title_query_param, required=False, location='query', schema=coreschema.String( title='Title', description=force_str( self.title_query_description))), coreapi.Field(name=self.content_query_param, required=False, location='query', schema=coreschema.String( title='Title', description=force_str( self.category_query_description))), coreapi.Field(name=self.audio_query_param, required=False, location='query', schema=coreschema.Boolean( title='Has Audio', description=force_str( self.audio_query_description))), coreapi.Field(name=self.from_year_query_param, required=False, location='query', schema=coreschema.Integer( title='From Year', description=force_str( self.from_year_query_description))), coreapi.Field(name=self.to_year_query_param, required=False, location='query', schema=coreschema.Integer( title='To Year', description=force_str( self.to_year_query_description))), coreapi.Field(name=self.sort_query_param, required=False, location='query', schema=coreschema.Enum( title='Sort by', description=force_str( self.sort_query_description), enum=[ 'publish_date', 'add_date', 'author', 'has_audio', 'pages', 'downloads', 'reads', 'rate', '-publish_date', '-add_date', '-author', '-has_audio', '-pages', '-downloads', '-reads', '-rate', ])), ] return fields
location='path', required=True, schema=coreschema.Integer()) ]), 'set_complete': Link(url='/todo/{ident}/', action='PUT', fields=[ Field(name='ident', location='path', required=True, schema=coreschema.Integer()), Field(name='complete', location='query', required=False, schema=coreschema.Boolean()) ]), 'set_percent_complete': Link(url='/todo/{ident}/percent_complete', action='PUT', fields=[ Field(name='ident', location='path', required=True, schema=coreschema.Integer()), Field(name='percent_complete', location='query', required=False, schema=coreschema.Number()) ]), 'set_category':
jsonschema = coreschema.RefSpace({ 'Schema': coreschema.Object( properties={ # Meta 'id': coreschema.String(format='uri'), '$schema': coreschema.String(format='uri'), 'title': coreschema.String(), 'description': coreschema.String(), 'default': coreschema.Anything(), 'definitions': coreschema.Ref('SchemaMap'), # Type 'type': coreschema.Ref('SimpleTypes') | coreschema.Array(items=coreschema.Ref('SimpleTypes'), min_items=1, unique_items=True), # Number validators 'minimum': coreschema.Number(), 'maximum': coreschema.Number(), 'exclusiveMinimum': coreschema.Boolean(default=False), 'exclusiveMaximum': coreschema.Boolean(default=False), 'multipleOf': coreschema.Number(minimum=0, exclusive_minimum=True), # String validators 'minLength': coreschema.Integer(minimum=0, default=0), 'maxLength': coreschema.Integer(minimum=0), 'pattern': coreschema.String(format='regex'), 'format': coreschema.String(), # Array validators 'items': coreschema.Ref('Schema') | coreschema.Ref('SchemaArray'), # TODO: default={} 'additionalItems': coreschema.Boolean() | coreschema.Ref('Schema'), # TODO: default={} 'minItems': coreschema.Integer(minimum=0, default=0), 'maxItems': coreschema.Integer(minimum=0), 'uniqueItems': coreschema.Boolean(default=False), # Object validators 'properties': coreschema.Ref('SchemaMap'),
expected = Schema(url='/schema/', content={ 'list_todo': Link( url='/todo/', action='GET', description='list_todo description', fields=[Field(name='search', location='query', required=False, schema=coreschema.String())] ), 'add_todo': Link( url='/todo/', action='POST', description='add_todo description\nMultiple indented lines', fields=[ Field(name='id', required=False, location='form', schema=coreschema.Integer()), Field(name='text', required=False, location='form', schema=coreschema.String()), Field(name='complete', required=False, location='form', schema=coreschema.Boolean()), Field(name='percent_complete', required=False, location='form', schema=coreschema.Number()), Field(name='category', required=False, location='form', schema=coreschema.Enum(enum=['shop', 'chore'])) ] ), 'show_todo': Link( url='/todo/{ident}/', action='GET', fields=[Field(name='ident', location='path', required=True, schema=coreschema.Integer())] ), 'set_complete': Link( url='/todo/{ident}/', action='PUT', fields=[ Field(name='ident', location='path', required=True, schema=coreschema.Integer()), Field(name='complete', location='query', required=False, schema=coreschema.Boolean())
class GoodsManageViewSet(viewsets.ViewSet): '''二手物品相关服务,需认证 错误码42xxx''' authentication_classes = [JWTAuthentication] permission_classes = [permissions.IsAuthenticated] schema = CustomSchema( manual_fields={ 'POST:/goods/publish/': [ coreapi.Field("title", required=True, location="form", description='标题 品类品牌型号都是买家喜欢搜索的'), coreapi.Field("description", required=True, location="form", description='描述宝贝的转手原因、入手渠道和使用感受'), coreapi.Field( "images", required=True, location="form", description='照片,每一项以 goodspublish 开头,多项之间以英文分号分隔'), coreapi.Field("isNew", required=True, location="form", description='是否 全新宝贝', schema=coreschema.Boolean()), coreapi.Field("knife", required=True, location="form", description='是否 可小刀', schema=coreschema.Boolean()), coreapi.Field( "price", required=True, location="form", description='价格'), coreapi.Field("category", required=True, location="form", description='分类,只有一级分类'), coreapi.Field("tags", required=True, location="form", description='标签,以井号(#)分隔'), ], 'POST:/goods/status/': [ coreapi.Field("gid", required=True, location="form", description='二手物品发布信息唯一标识 GID'), coreapi.Field( "status", required=True, location="form", description='改变状态:normal-重新发布、delete-删除、removed-下架、sold-出售。' ), ], 'POST:/goods/collect/': [ coreapi.Field("gid", required=True, location="form", description='二手物品发布信息唯一标识 GID'), coreapi.Field("status", required=True, location="form", description='收藏:normal;取消收藏:delete'), ], 'GET:/goods/collection/': [], 'POST:/goods/comment/': [ coreapi.Field("gid", required=True, location="form", description='二手物品发布信息唯一标识 GID'), coreapi.Field("toUser", required=False, location="form", description='回复的用户的 UID'), coreapi.Field("comment", required=True, location="form", description='评论内容'), ], 'POST:/goods/comment/delete/': [ coreapi.Field("cid", required=True, location="form", description='评论信息唯一标识'), ], }) @action(methods=['post'], detail=False) def publish(self, request, format=None): '''发布二手物品信息''' user = request.user # 获取及检查参数 params = [ 'title', 'description', 'images', 'isNew', 'knife', 'price', 'category', 'tags' ] title, description, images, isNew, knife, price, category, tags = get_dict_values( request.data, params) if check_none(title, description, images, isNew, knife, price, category, tags): raise ParseError('缺少参数:' + ', '.join(params)) # status 默认为 normal # 检查标签,将新标签存入数据库,旧标签使用次数加一 tags_arr = tags.split('#') for tag in tags_arr: try: record_tag = GoodsTags.objects.get(name=tag) record_tag.using = record_tag.using + 1 record_tag.save() except GoodsTags.DoesNotExist: record_tag_new = GoodsTags(name=tag, types='diy', using=1, createUser=user) record_tag_new.save() # 将分类标签使用次数加一 try: record_category = GoodsTags.objects.get(name=category) record_category.using = record_category.using + 1 record_category.save() except GoodsTags.DoesNotExist: pass # 反序列化数据 publish_params = { 'uid': user, 'title': title, 'description': description, 'images': images, 'isNew': isNew, 'knife': knife, 'price': price, 'category': category, 'tags': tags, } record_publish = PublishGoodsSerializers(data=publish_params) if not record_publish.is_valid(): errors = record_publish.errors return Response(res_format(errors, code=42001, msg='发布信息格式错误')) record_publish.save() return Response( res_format({'gid': record_publish.data['gid']}, msg='发布成功')) @action(methods=['post'], detail=False) def status(self, request, format=None): '''更改二手物品信息发布状态''' user = request.user # 获取及检查参数 params = ['gid', 'status'] gid, status = get_dict_values(request.data, params) if check_none(gid, status): raise ParseError('缺少参数:' + ', '.join(params)) # 检查是否是更新合法的图书状态 status_dict = { 'normal': '重新发布', 'delete': '删除', 'sold': '出售', 'removed': '下架' } if status not in status_dict: return Response( res_format(None, code=42002, msg='未知的切换状态 ' + status)) try: # 查询当前状态 record = PublishGoods.objects.get(gid=gid) # 判断这本书是不是该用户发布的 if record.uid != user: return Response(res_format(None, code=42003, msg='无法操作别人发布的物品')) # 检查发布信息的系统状态 if record.systemStatus != 'normal': return Response( res_format(None, code=42004, msg='该物品的系统状态异常,无法操作')) # 出售后只可以进行删除 if record.status == 'sold' and status != 'delete': return Response( res_format(None, code=42005, msg='不可对已出售的物品进行其他操作')) # 检查无误,可以切换状态了 record.status = status record.save() return Response(res_format(None, msg=status_dict[status] + '物品成功')) except PublishGoods.DoesNotExist: return Response(res_format(None, code=42006, msg='无相关物品信息')) @action(methods=['post'], detail=False) def collect(self, request, format=None): '''收藏二手物品信息''' user = request.user # 获取及检查参数 params = ['gid', 'status'] gid, status = get_dict_values(request.data, params) if check_none(gid, status): raise ParseError('缺少参数:' + ', '.join(params)) # 检查是否是更新合法的图书状态 status_dict = {'normal': '收藏', 'delete': '取消收藏'} if status not in status_dict: return Response( res_format(None, code=42006, msg='收藏状态错误 ' + status)) # 查询信息 try: record_goods = PublishGoods.objects.get(gid=gid) if record_goods.uid == user: return Response(res_format(None, code=42008, msg='无法收藏自己发布的物品')) except PublishGoods.DoesNotExist: return Response(res_format(None, code=42009, msg='无相关物品信息')) # 检查收藏状态 try: record = CollectGoods.objects.get(uid=str(user), gid=gid) record.status = status record.save() except CollectGoods.DoesNotExist: record_new = CollectGoods(uid=user, gid=record_goods, status=status) record_new.save() return Response(res_format(None, msg=status_dict[status] + '成功')) @action(detail=False) def collection(self, request, format=None): '''查询已收藏的二手物品信息''' user = request.user record = CollectGoods.objects.filter(uid=str(user)).filter( status='normal').filter(gid__status='normal').filter( gid__systemStatus='normal').order_by('-updateTime') result = CollectGoodsSerializers(record, many=True).data return Response(res_format(result)) @action(methods=['post'], detail=False) def comment(self, request, format=None): '''给二手物品信息留言''' user = request.user # 评论频率限制 # 每分钟 3 条,每小时 30 条,每天 200 条 now = datetime.datetime.now() limit_minute = now - datetime.timedelta(minutes=1) limit_hour = now - datetime.timedelta(hours=1) limit_day = now - datetime.timedelta(hours=24) record_limit = CommentGoods.objects.filter(commentUserInfo=user) if len(record_limit.filter(createTime__gt=limit_minute)) >= 3: return Response( res_format('Minute Limit', code=42010, msg='留言失败,请过一会儿再来')) if len(record_limit.filter(createTime__gt=limit_hour)) >= 30: return Response( res_format('Hour Limit', code=42011, msg='留言失败,请过一会儿再来')) if len(record_limit.filter(createTime__gt=limit_day)) >= 100: return Response( res_format('Day Limit', code=42012, msg='留言失败,请过一会儿再来')) # 获取及检查参数 params = ['gid', 'comment'] gid, comment = get_dict_values(request.data, params) if check_none(gid, status): raise ParseError('缺少参数:' + ', '.join(params)) # 检查评论信息 if len(comment) == 0: return Response(res_format(None, code=42013, msg='评论不能为空')) # 获取信息 try: record_goods = PublishGoods.objects.get(gid=gid) except PublishGoods.DoesNotExist: return Response(res_format(None, code=42014, msg='评论失败,无法获取物品信息')) # 获取回复别人评论的别人的用户信息 to_user = request.data.get('toUser', None) if to_user is not None: try: record_to_user = AccountBasic.objects.get(uid=to_user) except AccountBasic.DoesNotExist: record_to_user = None else: record_to_user = None # 保存评论 record = CommentGoods(commentUserInfo=user, toUserInfo=record_to_user, goodsInfo=record_goods, comment=comment) record.save() result = CommentGoodsSerializers(record).data return Response(res_format(result)) @action(methods=['post'], detail=False, url_path='comment/delete') def comment_delete(self, request, format=None): '''删除留言''' user = request.user cid = request.data.get('cid', None) if cid is None: return Response(res_format(None, code=42015, msg='删除评论失败')) # 查询评论 try: record = CommentGoods.objects.get(cid=cid, commentUserInfo=user) except CommentGoods.DoesNotExist: return Response(res_format(None, code=42016, msg='删除评论失败,未找到相关评论')) # 删除 record.status = 'delete' record.save() return Response(res_format(None))
class CMSAuthToken(viewsets.GenericViewSet): """Implements retrieving of Token.""" # permission_classes = (IsAuthenticated,) # from rest_framework.schemas.inspectors import AuthoSchema schema = ManualSchema( fields=[ coreapi.Field( "username", required=True, location="form", schema=coreschema.String( description="username required to create or retrieve token" ), ), coreapi.Field( "password", required=True, location="form", schema=coreschema.String( description="password required to create or retrieve token" ), ), coreapi.Field( "renew", required=False, location="query", schema=coreschema.Boolean( description= "set to true to retrieve a new token invalidating old one if it exists." ), description="password required to create or retrieve token", ), ], description="Gets or Creates a Token for the given user.", ) # def get(self, request): # if request.user.is_authenticated: # token = def retrieve(self, request, **kwargs): username = request.data.get("username", None) password = request.data.get("password", None) renew = request.data.get("renew", False) """Returns token for logged in user.""" if request.user.is_authenticated: if renew: try: token = Token.objects.get(user=request.user) token.delete() except ObjectDoesNotExist as e: # Nothing to renew pass token, created = Token.objects.get_or_create(user=request.user) return Response(data={"token": token.key}, status=status.HTTP_200_OK) else: return Response(data={"error": "Not Authorized"}, status=status.HTTP_401_UNAUTHORIZED)
def get_serializer_fields(self, path, method): fields = [] if method == 'POST': fields = [ coreapi.Field(name="group_id", required=True, location="form", schema=coreschema.Integer( title="group", description="Id группы"), description='group_id'), coreapi.Field( name="title", required=True, location="form", schema=coreschema.String(title="title", description="Название покупки"), description='Название покупки', ), coreapi.Field( name="price", required=True, location="form", schema=coreschema.Number(title="price", description="Цена"), description='Цена', ), coreapi.Field( name="comment", required=True, location="form", schema=coreschema.String(title="comment", description="Комментарий"), description='Комментарий', ), coreapi.Field( name="user", required=True, location="form", schema=coreschema.Array(title="user", description="Юзеры в покупке"), description='Юзеры в покупке', ), ] if method == 'PUT': fields = [ coreapi.Field(name="done", required=True, location="form", schema=coreschema.Boolean( title="done", description="Завершено"), description='Завершено'), coreapi.Field( name="title", required=True, location="form", schema=coreschema.String(title="title", description="Название покупки"), description='Название покупки', ), coreapi.Field( name="price", required=True, location="form", schema=coreschema.Number(title="price", description="Цена"), description='Цена', ), coreapi.Field( name="comment", required=True, location="form", schema=coreschema.String(title="comment", description="Комментарий"), description='Комментарий', ), coreapi.Field( name="user", required=True, location="form", schema=coreschema.Array(title="user", description="Юзеры в покупке"), description='Юзеры в покупке', ), ] return fields
coreschema.Anything(), "definitions": coreschema.Ref("SchemaMap"), # Type "type": coreschema.Ref("SimpleTypes") | coreschema.Array(items=coreschema.Ref("SimpleTypes"), min_items=1, unique_items=True), # Number validators "minimum": coreschema.Number(), "maximum": coreschema.Number(), "exclusiveMinimum": coreschema.Boolean(default=False), "exclusiveMaximum": coreschema.Boolean(default=False), "multipleOf": coreschema.Number(minimum=0, exclusive_minimum=True), # String validators "minLength": coreschema.Integer(minimum=0, default=0), "maxLength": coreschema.Integer(minimum=0), "pattern": coreschema.String(format="regex"), "format": coreschema.String(), # Array validators "items":
class BookManageViewSet(viewsets.ViewSet): '''图书管理相关服务 需认证 错误码23xxx''' authentication_classes = [JWTAuthentication] permission_classes = [permissions.IsAuthenticated] schema = CustomSchema( manual_fields={ 'POST:/book/publish/': [ coreapi.Field("description", required=True, location="form", description='用户描述,不可为空,最多 300 字'), coreapi.Field("price", required=True, location="form", description='出售价格'), coreapi.Field("images", required=True, location="form", description='图书图片'), coreapi.Field("knife", required=True, location="form", description='是否可小刀', schema=coreschema.Boolean()), coreapi.Field("phase", required=True, location="form", description='品相,L10/L9/L7/L5'), coreapi.Field("status", required=True, location="form", description='发布状态,normal:正常;draft:草稿'), coreapi.Field("bookISBN", required=True, location="form", description='图书 ISBN 码'), coreapi.Field("categoryL1", required=True, location="form", description='图书类别,大类'), coreapi.Field("categoryL2", required=True, location="form", description='图书类别,小类'), ], 'POST:/book/status/': [ coreapi.Field("bid", required=True, location="form", description='图书发布信息唯一标识 BID'), coreapi.Field( "status", required=True, location="form", description='改变状态:normal-重新发布、delete-删除、removed-下架、sold-出售。' ), ], 'POST:/book/collect/': [ coreapi.Field("bid", required=True, location="form", description='图书发布信息唯一标识 BID'), coreapi.Field("status", required=True, location="form", description='收藏:normal;取消收藏:delete'), ], 'GET:/book/collection/': [], 'POST:/book/comment/': [ coreapi.Field("bid", required=True, location="form", description='图书发布信息唯一标识 BID'), coreapi.Field("toUser", required=False, location="form", description='回复的用户的 UID'), coreapi.Field("comment", required=True, location="form", description='评论内容'), ], 'POST:/book/comment/delete/': [ coreapi.Field("cid", required=True, location="form", description='评论信息唯一标识'), ], }) @action(methods=['post'], detail=False, url_path='publish') def publish(self, request, format=None): '''发布图书信息''' user = request.user # 获取及检查参数 params = [ 'description', 'price', 'images', 'knife', 'phase', 'status', 'bookISBN', 'categoryL1', 'categoryL2' ] description, price, images, knife, phase, status, bookISBN, categoryL1, categoryL2 = get_dict_values( request.data, params) if check_none(description, price, images, knife, phase, status, bookISBN, categoryL1, categoryL2): raise ParseError('缺少参数:' + ', '.join(params)) # 发布状态,normal:正常;draft:草稿 if status not in ['normal', 'draft']: return Response(res_format(None, code=23003, msg='发布失败,发布状态错误')) # 检查发布信息中是否有 bookInfo,如果有就是用户完善的信息 # 没有就通过 ISBN 查询图书信息 user_isbn_info = request.data.get('bookInfo', None) if user_isbn_info is None: dataSource = 'douban' try: record_isbn = ISBNInfo.objects.get(isbn=bookISBN) isbn_info = ISBNInfoSerializers(record_isbn).data except ISBNInfo.DoesNotExist: return Response( res_format(None, code=23001, msg='发布失败,请重新扫描 ISBN 码获取信息')) else: dataSource = 'user' # 将空字符串设置为 None for key in user_isbn_info: if user_isbn_info[key] == '': user_isbn_info[key] = None # 序列化数据,并校验 isbn_info_serializer = ISBNInfoSerializers(data=user_isbn_info) if not isbn_info_serializer.is_valid(): return Response( res_format(None, code=23011, msg='完善的图书信息格式错误,请修改')) else: isbn_info = isbn_info_serializer.data # 发布信息模块 publish_params = { 'uid': user, 'bookTitle': (isbn_info['title'] if isbn_info['title'] is not None else '') + (' : ' + isbn_info['subtitle'] if isbn_info['subtitle'] is not None else ''), 'bookAuthor': isbn_info['author'], 'bookCover': isbn_info['cover'], 'bookPress': isbn_info['press'], 'bookPublishYear': isbn_info['publishYear'], 'bookPrice': isbn_info['price'], 'bookIntroduction': isbn_info['introduction'][:512] if isbn_info['introduction'] else None, 'bookRatingValue': isbn_info['ratingValue'], 'dataSource': dataSource } # 如果该图书没有标题,就设为 None if publish_params['bookTitle'] == '': publish_params['bookTitle'] = None publish_params.update(request.data) record_publish = PublishBookSerializers(data=publish_params) if not record_publish.is_valid(): errors = record_publish.errors return Response(res_format(errors, code=23002, msg='发布信息格式错误')) record_publish.save() return Response( res_format({'bid': record_publish.data['bid']}, msg='发布成功')) @action(methods=['post'], detail=False, url_path='status') def status(self, request, format=None): '''更新图书状态,normal-重新发布、delete-删除、removed-下架''' user = request.user # 获取及检查参数 params = ['bid', 'status'] bid, status = get_dict_values(request.data, params) if check_none(bid, status): raise ParseError('缺少参数:' + ', '.join(params)) # 检查是否是更新合法的图书状态 status_dict = { 'normal': '重新发布', 'delete': '删除', 'sold': '出售', 'removed': '下架' } if status not in status_dict: return Response( res_format(None, code=23006, msg='未知的切换状态 ' + status)) try: # 查询当前图书状态 record = PublishBook.objects.get(bid=bid) # 判断这本书是不是该用户发布的 if record.uid != user: return Response(res_format(None, code=23005, msg='无法操作别人发布的图书')) # 检查图书发布信息的系统状态 if record.systemStatus != 'normal': return Response( res_format(None, code=23007, msg='该图书的系统状态异常,无法操作')) # 出售后的图书只可以进行删除 if record.status == 'sold' and status != 'delete': return Response( res_format(None, code=23019, msg='不可对已出售的图书进行其他操作')) # 检查无误,可以切换状态了 record.status = status record.save() return Response(res_format(None, msg=status_dict[status] + '图书成功')) except PublishBook.DoesNotExist: return Response(res_format(None, code=23004, msg='无相关图书信息')) @action(methods=['post'], detail=False, url_path='collect') def collect(self, request, format=None): '''收藏/取消收藏图书''' user = request.user # 获取及检查参数 params = ['bid', 'status'] bid, status = get_dict_values(request.data, params) if check_none(bid, status): raise ParseError('缺少参数:' + ', '.join(params)) # 检查是否是更新合法的图书状态 status_dict = {'normal': '收藏', 'delete': '取消收藏'} if status not in status_dict: return Response( res_format(None, code=23008, msg='收藏状态错误 ' + status)) # 查询图书信息 try: record_book = PublishBook.objects.get(bid=bid) if record_book.uid == user: return Response(res_format(None, code=23010, msg='无法收藏自己发布的图书')) except PublishBook.DoesNotExist: return Response(res_format(None, code=23009, msg='无相关图书信息')) # 检查图书收藏状态 try: record = CollectBook.objects.get(uid=str(user), bid=bid) record.status = status record.save() except CollectBook.DoesNotExist: record_new = CollectBook(uid=user, bid=record_book, status=status) record_new.save() return Response(res_format(None, msg=status_dict[status] + '成功')) @action(detail=False, url_path='collection') def collection(self, request, format=None): '''获取我的收藏信息''' user = request.user record = CollectBook.objects.filter(uid=str(user)).filter( status='normal').filter(bid__status='normal').filter( bid__systemStatus='normal').order_by('-updateTime') result = CollectBookOverviewSerializers(record, many=True).data return Response(res_format(result)) @action(methods=['post'], detail=False, url_path='comment') def comment(self, request, format=None): '''对图书信息发表评论''' user = request.user # 评论频率限制 # 每分钟 3 条,每小时 30 条,每天 200 条 now = datetime.datetime.now() limit_minute = now - datetime.timedelta(minutes=1) limit_hour = now - datetime.timedelta(hours=1) limit_day = now - datetime.timedelta(hours=24) record_limit = CommentBook.objects.filter(commentUserInfo=user) if len(record_limit.filter(createTime__gt=limit_minute)) >= 3: return Response( res_format('Minute Limit', code=23016, msg='留言失败,请过一会儿再来')) if len(record_limit.filter(createTime__gt=limit_hour)) >= 30: return Response( res_format('Hour Limit', code=23017, msg='留言失败,请过一会儿再来')) if len(record_limit.filter(createTime__gt=limit_day)) >= 100: return Response( res_format('Day Limit', code=23018, msg='留言失败,请过一会儿再来')) # 获取及检查参数 params = ['bid', 'comment'] bid, comment = get_dict_values(request.data, params) if check_none(bid, status): raise ParseError('缺少参数:' + ', '.join(params)) # 检查评论信息 if len(comment) == 0: return Response(res_format(None, code=23012, msg='评论不能为空')) # 获取图书信息 try: record_book = PublishBook.objects.get(bid=bid) except PublishBook.DoesNotExist: return Response(res_format(None, code=23013, msg='评论失败,无法获取图书信息')) # 获取回复别人评论的别人的用户信息 to_user = request.data.get('toUser', None) if to_user is not None: try: record_to_user = AccountBasic.objects.get(uid=to_user) except AccountBasic.DoesNotExist: record_to_user = None else: record_to_user = None # 保存评论 record = CommentBook(commentUserInfo=user, toUserInfo=record_to_user, bookInfo=record_book, comment=comment) record.save() result = CommentBookInfoSerializers(record).data return Response(res_format(result)) @action(methods=['post'], detail=False, url_path='comment/delete') def comment_delete(self, request, format=None): '''删除图书评论''' user = request.user cid = request.data.get('cid', None) if cid is None: return Response(res_format(None, code=23014, msg='删除评论失败')) # 查询评论 try: record = CommentBook.objects.get(cid=cid, commentUserInfo=user) except CommentBook.DoesNotExist: return Response(res_format(None, code=23015, msg='删除评论失败,未找到相关评论')) # 删除 record.status = 'delete' record.save() return Response(res_format(None))
class UserView(ModelViewSetNoDelete): queryset = User.objects.all() permission_classes = [AllowAny] filterset_class = UserFilters def get_serializer_class(self): if self.request.method == "GET": return UserSerializer else: return UserSerializerPost def get_permissions(self): """ Instantiates and returns the list of permissions that this view requires. """ if self.action == "create" or self.action == "reset_password": permission_classes = [AllowAny] else: permission_classes = [IsAuthenticated] return [permission() for permission in permission_classes] @action( methods=["get"], detail=False, url_path="current", schema=ManualSchema( fields=[], description="Get the data from the currently logged user"), ) def get_current_user_data(self, request: Request): """ Get the data from the currently logged user """ return Response(data=UserSerializerCurrentUser(request.user).data) @action( methods=["get"], detail=True, url_path="getaddresses", schema=ManualSchema( description="Gets all addresses from a user", fields=[ coreapi.Field( "id", required=True, location="path", schema=coreschema.Integer(), description="User ID", ), coreapi.Field( "active", required=False, location="query", schema=coreschema.Boolean(), description="Active addresses", ), ], ), ) def get_addresses(self, request: Request, pk: int): user = self.get_object() active = request.query_params.get("active", None) addresses_queryset = user.addresses.all().select_related( "neighborhood", "neighborhood__city", "neighborhood__city__state") if active is not None: active = str_to_boolean(active) addresses_queryset = addresses_queryset.filter(active=active) return Response( data=UserAddressSerializer(addresses_queryset, many=True).data) @action( methods=["patch"], detail=True, url_path="changeaddressstatus", schema=ManualSchema( description="Updates the status of an address from the given user", fields=[ coreapi.Field( "id", required=True, location="path", schema=coreschema.Integer(), description="User ID", ), coreapi.Field( "address_id", required=False, location="form", schema=coreschema.Integer(), description="Adress' ID", ), coreapi.Field( "active", required=False, location="form", schema=coreschema.Boolean(), description="New value for the address' active attribute", ), ], ), ) def change_address_status(self, request: Request, pk: int): user = self.get_object() address_id = get_param_or_400(request.data, "address_id", int) active = get_param_or_400(request.data, "active", bool) address = user.addresses.get(id=address_id) address.active = active address.save() return Response() @action( methods=["post"], detail=True, url_path="addaddress", schema=ManualSchema( description="Add a new addresses to the given user", fields=[ coreapi.Field( "id", required=True, location="path", schema=coreschema.Integer(), description="User ID", ), coreapi.Field( "neighborhood_id", required=False, location="form", schema=coreschema.Integer(), description="Neighborhood' ID", ), coreapi.Field( "address", required=False, location="form", schema=coreschema.String(max_length=150), description="State' ID", ), coreapi.Field( "zip", required=False, location="form", schema=coreschema.String(max_length=8), description="ZIP code", ), ], ), ) @transaction.atomic def add_address(self, request: Request, pk: int): user = self.get_object() neighborhood_id = get_param_or_400(request.data, "neighborhood_id", int) address = get_param_or_400(request.data, "address", str) zip_code = get_param_or_400(request.data, "zip", str) addresses_queryset = UserAddress.objects.all().select_related( "neighborhood", "neighborhood__city", "neighborhood__city__state") new_address = addresses_queryset.create( neighborhood_id=neighborhood_id, address=address, zip_code=zip_code) user.addresses.add(new_address) user.save() return Response(data=UserAddressSerializer(new_address).data) @action( methods=["patch"], detail=True, url_path="updateaddress", schema=ManualSchema( description="Updates a user address", fields=[ coreapi.Field( "id", required=True, location="path", schema=coreschema.Integer(), description="User ID", ), coreapi.Field( "user_address_id", required=False, location="form", schema=coreschema.Integer(), description="User address ID", ), coreapi.Field( "neighborhood_id", required=False, location="form", schema=coreschema.Integer(), description="Neighborhood' ID", ), coreapi.Field( "address", required=False, location="form", schema=coreschema.String(max_length=150), description="State' ID", ), coreapi.Field( "zip", required=False, location="form", schema=coreschema.String(max_length=8), description="ZIP code", ), ], ), ) @transaction.atomic def update_address(self, request: Request, pk: int): user = self.get_object() address_id = get_param_or_400(request.data, "user_address_id", int) neighborhood_id = get_param_or_400(request.data, "neighborhood_id", int) address = get_param_or_400(request.data, "address", str) zip_code = get_param_or_400(request.data, "zip", str) user_address = user.addresses.get(id=address_id) user_address.neighborhood_id = neighborhood_id user_address.zip_code = zip_code user_address.address = address user_address.save() return Response() @action( methods=["post"], detail=False, url_path="resetpassword", schema=ManualSchema( description="User e-mail", fields=[ coreapi.Field( "email", required=True, location="form", schema=coreschema.String(), description="User e-mail", ), ], ), ) @transaction.atomic def reset_password(self, request: Request): user = User.objects.all() email = get_param_or_400(request.data, "email", str) try: user_reset_pass = user.get(email=email) if user_reset_pass: new_pass = generate_new_pass(user_reset_pass) user_reset_pass.set_password(new_pass) user_reset_pass.save() email_render_data = { 'user': user_reset_pass.first_name, 'new_password': new_pass } user_reset_pass.send_email( 'emails/user_reset_password.html', email_render_data, 'Conta Comigo APP - Recuperação de Senha') return Response( data=UserNewPasswordSerializer(user_reset_pass).data) # return Response(data={"password": new_pass}) except Exception as ex: raise ValueError(f"There is no register for {email}: {ex}") return Response()