一 人脸识别
1.1 使用步骤
https: // cloud. baidu. com/ product/ face. html
1.2 上传-删除-匹配人脸
from aip import AipFace
import base64
from pypinyin import lazy_pinyin, Style
class BaiDuAI : def __init__ ( self, APP_ID= '62643893' , API_KEY= 'hfAwQUE1fwyCjXG01ZCHbaSG' , SECRET_KEY= 'qgxJneAt0ovmGA2rLrdq99GEFs1apjte' ) : """ 你的 APPID AK SK """ self. APP_ID = APP_IDself. API_KEY = API_KEYself. SECRET_KEY = SECRET_KEYself. client = AipFace( self. APP_ID, self. API_KEY, self. SECRET_KEY) def name_to_pinyin ( self, text) : style = Style. TONE3name_list= lazy_pinyin( text, style= style) return '' . join( name_list) def add_user ( self) : data = base64. b64encode( open ( './gtl.png' , 'rb' ) . read( ) ) . decode( 'utf-8' ) image = dataimageType = "BASE64" groupId = "100" userId= self. name_to_pinyin( '古天乐' ) """ 调用人脸注册 """ self. client. addUser( image, imageType, groupId, userId) ; """ 如果有可选参数 """ options = { } options[ "user_info" ] = "彭于晏" options[ "quality_control" ] = "NORMAL" options[ "liveness_control" ] = "LOW" options[ "action_type" ] = "REPLACE" """ 带参数调用人脸注册 """ self. client. addUser( image, imageType, groupId, userId, options) def search ( self) : data = base64. b64encode( open ( './pyy2.png' , 'rb' ) . read( ) ) . decode( 'utf-8' ) image = dataimageType = "BASE64" groupIdList = "100,2" """ 调用人脸搜索 """ self. client. search( image, imageType, groupIdList) ; """ 如果有可选参数 """ options = { } options[ "match_threshold" ] = 70 options[ "quality_control" ] = "NORMAL" options[ "liveness_control" ] = "LOW" options[ "max_user_num" ] = 3 """ 带参数调用人脸搜索 """ res= self. client. search( image, imageType, groupIdList, options) print ( res) def delete ( self) : userId = "user1" groupId = "group1" faceToken = "face_token_23123" """ 调用人脸删除 """ self. client. faceDelete( userId, groupId, faceToken) ;
if __name__ == '__main__' : ai= BaiDuAI( ) ai. search( )
1.3 项目中集成
class CollectionSaveSerializer ( serializers. ModelSerializer) : class Meta : model = Collectionfields = [ 'name' , 'avatar' , 'area' ] def create ( self, validated_data) : from libs. baidu_ai import BaiDuAIbaidu= BaiDuAI( ) avatar_file_object = validated_data. get( 'avatar' ) print ( avatar_file_object) name = validated_data. get( 'name' ) name_pinyin= baidu. name_to_pinyin( name) res= baidu. add_user( avatar_file_object, name, name_pinyin) validated_data[ 'name_pinyin' ] = name_pinyinvalidated_data[ 'face_token' ] = res. get( 'result' ) . get( 'face_token' ) instance= super ( ) . create( validated_data) return instance
def destroy ( self, request, * args, ** kwargs) : from libs. baidu_ai import BaiDuAIinstance = self. get_object( ) baidu= BaiDuAI( ) res= baidu. delete( instance. name_pinyin, face_token= instance. face_token) print ( res) self. perform_destroy( instance) return Response( )
二 语音识别
2.1 收费方案
def speed ( file_object) : from aip import AipSpeechAPP_ID = '' API_KEY = '' SECRET_KEY = '' client = AipSpeech( APP_ID, API_KEY, SECRET_KEY) data = file_object. read( ) return client. asr( data, 'pcm' , 16000 , { 'dev_pid' : 1537 } )
2.2 免费方案
\site- packages\speech_recognition\pocketsphinx- data
1 . WAV
2 . AIFF/ AIFF- C
3 . FLAC
import speech_recognition as sraudio_file = './test1.wav' r = sr. Recognizer( ) with sr. AudioFile( audio_file) as source: audio = r. record( source)
result = r. recognize_sphinx( audio, language= "zh-CN" )
print ( result)
三 采集统计
小程序端statistics
< view class = "container" > < view class = "menu" wx: for = "{{dataList}}" wx: key= "index" > < view> < label class = "iconfont icon-SCHEDULE" > < / label> { { item. date} } < / view> < label> { { item. count} } 个< / label> < / view> < / view>
. container{ border- top: 1px solid
} . container . menu{ font- size: small; padding: 10px 40rpx; border- bottom: 1px dotted text- align: center; display: flex; flex- direction: row; justify- content: space- between; background- color: white;
}
var app = getApp( ) ;
var api = require( "../../config/settings.js" ) Page( { / ** * 页面的初始数据* / data: { dataList: [ { 'date' : '2024年4月20日' , 'count' : 22 } , { 'date' : '2024年4月21日' , 'count' : 12 } , { 'date' : '2024年4月22日' , 'count' : 232 } ] } , getRecord: function( ) { wx. showLoading( { mask: true} ) wx. request( { url: api. statistics, method: "GET" , success : ( res) = > { this. setData( { dataList: res. data} ) } , complete: ( ) = > { wx. hideLoading( ) } } ) } , / ** * 生命周期函数- - 监听页面加载* / onLoad( options) { this. getRecord( ) ; } , / ** * 页面相关事件处理函数- - 监听用户下拉动作* / onPullDownRefresh( ) { this. getRecord( ) ; } , } )
{ "usingComponents" : { } , "navigationBarTitleText" : "采集统计" , "enablePullDownRefresh" : true
}
后端接口
class StatisticsView ( GenericViewSet, ListModelMixin) : queryset = Collection. objects. annotate( date= Trunc( 'create_time' , 'day' ) ) . values( 'date' ) . annotate( count= Count( 'id' ) ) . values( 'date' , 'count' ) serializer_class = StatisticsListSerializer
class StatisticsListSerializer ( serializers. Serializer) : date = serializers. DateTimeField( format = "%Y年%m月%d日" ) count = serializers. IntegerField( )
class Collection ( models. Model) : name = models. CharField( max_length= 32 , verbose_name= '采集人员姓名' ) name_pinyin= models. CharField( max_length= 32 , verbose_name= '姓名拼音' , null= True ) avatar = models. ImageField( upload_to= 'collection/%Y/%m/%d/' , default= 'default.png' , verbose_name= '头像' ) create_time = models. DateTimeField( verbose_name= '采集时间' , default= datetime. now( ) ) face_token= models. CharField( max_length= 128 , verbose_name= '百度ai的Token' , null= True ) area = models. ForeignKey( to= 'Area' , null= True , verbose_name= '网格区域' , on_delete= models. CASCADE) class Meta : verbose_name_plural = '采集表' def __str__ ( self) : return self. name
四 人脸检测
4.1 小程序端
< !- - pages/ face/ face. wxml- - >
< view class = "header" > < camera class = "camera" device- position= "{{ backFront ? 'back' : 'front' }}" flash= "off" frame- size= "medium" > < / camera> < view class = "switch" bindtap= "switchCamera" > < image src= "/images/camera/rotate-camera-white.png" > < / image> < / view> < button class = "submit" bindtap= "takePhoto" > 拍照检测 < / button>
< / view> < view class = "table" > < view class = "item" > < view class = "title" > 检测记录< / view> < / view> < view class = "item" wx: for = "{{record}}" wx: for - item= "row" wx: key= "index" > < view class = "record" > < view class = "avatar" > < image src= "{{row.avatar}}" > < / image> < / view> < view class = "desc" > < view wx: if = "{{row.code == 100}}" class = "username" > 检测成功:{ { row. user_id} } < / view> < view wx: else class = "username" > 检测失败: { { row. msg} } < / view> < view> < view class = "txt-group" > < label class = "zh" > { { row. error_msg} } < / label> < / view> < / view> < / view> < view class = "delete" > < block wx: if = "{{row.code == 100}}" > < label class = "iconfont icon-ziyuanxhdpi" style= "color:green" > < / label> < / block> < block wx: else > < label class = "iconfont icon-ziyuanxhdpi" style= "color:red" > < / label> < / block> < / view> < / view> < / view> < / view>
/ * pages/ face/ face. wxss * /
. header{ position: relative;
}
. camera{ height: 600rpx; width: 100 % ;
} . switch{ position: absolute; top: 10rpx; right: 20rpx; height: 80rpx; width: 80rpx;
} . switch image{ height: 100 % ; width: 100 % ;
} . submit{ margin- top: 40rpx; color: border: 2rpx solid background- color: font- size: 32rpx; font- weight: normal;
} . table{ margin- top: 40rpx; border- top: 1rpx solid
} . table . item { border- bottom: 1rpx solid } . table . item . title{ margin: 20rpx 30rpx; padding- left: 10rpx; border- left: 5rpx solid font- size: 26rpx;
} . record{ margin: 10rpx 40rpx; display: flex; flex- direction: row; justify- content: space- between;
} . record . avatar{ width: 100rpx; height: 100rpx;
} . record . avatar image{ width: 100 % ; height: 100 % ; border- radius: 30rpx;
} . record . desc{ margin: 0 40rpx;
}
. desc{ width: 290rpx; display: flex; flex- direction: column; justify- content: space- around;
}
. desc . username{ font- size: 25rpx;
} . txt- group{ font- size: 20rpx; margin: 5rpx 0 ;
}
. txt- group . zh{ color:
} . txt- group . en{ color:
} . area{ color: font- weight: bold;
} . delete{ width: 100rpx; text- align: center; display: flex; flex- direction: column; justify- content: center;
}
var api = require( "../../config/settings.js" ) Page( { data: { backFront: true, record: [ ] } , switchCamera( e) { var old = this. data. backFrontthis. setData( { backFront: !old} ) } , takePhoto( e) { wx. showLoading( { title: '检测中' , mask: true} ) const ctx = wx. createCameraContext( ) ctx. takePhoto( { quality: 'high' , success: ( res) = > { wx. uploadFile( { url: api. face, filePath: res. tempImagePath, name: 'avatar' , success: ( response) = > { let resdata = JSON. parse( response. data) console. log( resdata) if ( resdata. code== 100 | | resdata. code== 102 ) { console. log( resdata) resdata. avatar = res. tempImagePathvar oldRecord = this. data. recordoldRecord. unshift( resdata) console. log( oldRecord) this. setData( { record: oldRecord} ) } else { wx. showToast( { title: '请正常拍照' } ) } } , complete: function( ) { wx. hideLoading( ) } } ) } } ) } , } )
{ "usingComponents" : { } , "navigationBarTitleText" : "人脸检测"
}
4.2 后端
class FaceView ( GenericViewSet) : def create ( self, request, * args, ** kwargs) : avatar_object = request. data. get( 'avatar' ) if not avatar_object: return Response( { "msg" : "未提交图像" , "code" : 101 } ) from libs. baidu_ai import BaiDuAIai = BaiDuAI( ) result = ai. search( avatar_object) if result. get( 'error_code' ) == 0 : user = result. get( 'result' ) . get( 'user_list' ) [ 0 ] user_info = user. get( 'user_info' ) user_id = user. get( 'user_id' ) score = user. get( 'score' ) return Response( { "code" : 100 , 'msg' : '匹配成功' , 'user_info' : user_info, 'user_id' : user_id, 'score' : score, 'avatar' : '' } ) else : return Response( { "code" : 102 , 'msg' : '匹配失败,该人员可能不是我社区人员,注意防范' } )
from aip import AipFace
import base64
from pypinyin import lazy_pinyin, Styleclass BaiDuAI : def __init__ ( self, APP_ID= '62643893' , API_KEY= 'hfAwQUE1fwyCjXG01ZCHbaSG' , SECRET_KEY= 'qgxJneAt0ovmGA2rLrdq99GEFs1apjte' ) : """ 你的 APPID AK SK """ self. APP_ID = APP_IDself. API_KEY = API_KEYself. SECRET_KEY = SECRET_KEYself. client = AipFace( self. APP_ID, self. API_KEY, self. SECRET_KEY) def name_to_pinyin ( self, text) : style = Style. TONE3name_list = lazy_pinyin( text, style= style) return '' . join( name_list) def add_user ( self, path, name, userId, groupId= 100 ) : image = base64. b64encode( path. read( ) ) . decode( 'utf-8' ) imageType = "BASE64" """ 调用人脸注册 """ res= self. client. addUser( image, imageType, groupId, userId) ; print ( res) return resdef search ( self, img_obj) : image = base64. b64encode( img_obj. read( ) ) . decode( 'utf-8' ) image_type = "BASE64" group_id_list = "100,101" """ 调用人脸搜索 """ res= self. client. search( image, image_type, group_id_list) ; print ( res) return resdef delete ( self, user_id, face_token, group_id= 100 ) : """ 调用人脸删除 """ res= self. client. faceDelete( user_id, group_id, face_token) return resif __name__ == '__main__' : ai = BaiDuAI( ) ai. search( )
五 语音识别
5.1 小程序前端
// https: // developers. weixin. qq. com/ miniprogram/ dev/ api/ media/ recorder/ RecorderManager. start. html
const recorderManager = wx. getRecorderManager( )
var api = require( "../../config/settings.js" ) Page( { / ** * 页面的初始数据* / data: { content: "" , record: false} , recordStart: function( ) { this. setData( { record: true} ) const options = { // duration: 6000 , // 指定录音的时长,单位 mssampleRate: 16000 , // 采样率numberOfChannels: 1 , // 录音通道数encodeBitRate: 48000 , // 编码码率format : 'wav' // 音频格式,有效值 } // 开始录音recorderManager. start( options) } , recordCancel: function( ) { console. log( "停止" ) ; this. setData( { record: false} ) wx. hideLoading( ) } , recordStop: function( ) { if ( !this. data. record) { return } recorderManager. stop( ) ; recorderManager. onStop( ( res) = > { // this. tempFilePath = res. tempFilePathwx. showLoading( ) wx. uploadFile( { filePath: res. tempFilePath, name: 'voice' , url: api. voice, success: ( response) = > { console. log( response) // { 'code' : 100 , 'msg' : '成功' , 'result' : [ '欢迎欢迎' ] } let voiceResponse = JSON. parse( response. data) if ( voiceResponse. code == 100 ) { console. log( voiceResponse) this. setData( { content: this. data. content + voiceResponse. result[ 0 ] } ) } else { wx. showToast( { title: '识别失败,请重新操作!' , icon: "none" } ) } } , complete: ( ) = > { wx. hideLoading( ) } } , ) } ) this. setData( { record: false} ) } , } )
< !- - pages/ voice/ voice. wxml- - >
< textarea class = "text" placeholder= "等待语音识别自动录入..." placeholder- class = "hoder" model: value= "{{content}}" maxlength= "{{-1}}" > < / textarea> < button class = "btn" hover- class = "press" bind: longpress= "recordStart" bind: touchcancel= "recordCancel" bind: touchend= "recordStop" > < label class = "fa fa-microphone" > < / label> 按住说话< / button>
/ * pages/ voice/ voice. wxss * /
page{ background- color:
}
. text{ height: 400rpx; background- color: white; width: 100 % ; padding: 20rpx;
} . btn{ margin- top: 30rpx; / * color: border: 2rpx solid background- color: white; font- size: 32rpx; font- weight: normal;
} . press label{ color:
}
. press{ background- color:
}
. hoder{ font- size: 28rpx;
}
{ "usingComponents" : { } , "navigationBarTitleText" : "语音识别"
}
5.2 后端
from libs. baidu_ai import BaiDuVoiceclass VoiceView ( GenericViewSet) : def create ( self, request, * args, ** kwargs) : voice_object = request. data. get( 'voice' ) ai = BaiDuVoice( ) result = ai. speed( voice_object) if result. get( 'err_no' ) == 0 : return Response( { 'code' : 100 , 'msg' : '识别成功' , 'result' : result. get( 'result' ) } ) else : return Response( { 'code' : 101 , 'msg' : '识别失败' } )
from aip import AipFace
import base64
from pypinyin import lazy_pinyin, Style
from aip import AipSpeechclass BaiDuAI : def __init__ ( self, APP_ID= '62643893' , API_KEY= 'hfAwQUE1fwyCjXG01ZCHbaSG' , SECRET_KEY= 'qgxJneAt0ovmGA2rLrdq99GEFs1apjte' ) : """ 你的 APPID AK SK """ self. APP_ID = APP_IDself. API_KEY = API_KEYself. SECRET_KEY = SECRET_KEYself. client = AipFace( self. APP_ID, self. API_KEY, self. SECRET_KEY) def name_to_pinyin ( self, text) : style = Style. TONE3name_list = lazy_pinyin( text, style= style) return '' . join( name_list) def add_user ( self, path, name, userId, groupId= 100 ) : image = base64. b64encode( path. read( ) ) . decode( 'utf-8' ) imageType = "BASE64" """ 调用人脸注册 """ res= self. client. addUser( image, imageType, groupId, userId) ; print ( res) return resdef search ( self, img_obj) : image = base64. b64encode( img_obj. read( ) ) . decode( 'utf-8' ) image_type = "BASE64" group_id_list = "100,101" """ 调用人脸搜索 """ res= self. client. search( image, image_type, group_id_list) ; print ( res) return resdef delete ( self, user_id, face_token, group_id= 100 ) : """ 调用人脸删除 """ res= self. client. faceDelete( user_id, group_id, face_token) return resclass BaiDuVoice : def __init__ ( self, APP_ID= '63701411' , API_KEY= 'b0tYjKxPmcuoSm4SAen19p2c' , SECRET_KEY= 'm8ramzeLtMLHEa9XRPXQJNbcAWGiIuZ8' ) : """ 你的 APPID AK SK """ self. APP_ID = APP_IDself. API_KEY = API_KEYself. SECRET_KEY = SECRET_KEYself. client = AipSpeech( self. APP_ID, self. API_KEY, self. SECRET_KEY) def speed ( self, voice_object) : res= self. client. asr( voice_object. read( ) , 'pcm' , 16000 , { 'dev_pid' : 1537 , } ) return resif __name__ == '__main__' : ai = BaiDuVoice( ) file = open ( '../a.wav' , 'rb' ) res= ai. speed( file ) print ( res)
六 公告
6.1 微信小程序端
const api = require( "../../config/settings.js" )
Page( { data: { noticeList: [ { title: '公告标题1' , create_time: '2024-04-25' , content: '公告内容描述1,公告内容描述1,公告内容描述1。' , // 可以根据实际情况添加更多内容igm: '/images/notice/notice1.jpg' // 图片路径,根据实际情况修改} , { title: '公告标题2' , create_time: '2024-04-26' , content: '公告内容描述2,公告内容描述2,公告内容描述2。' , // 可以根据实际情况添加更多内容igm: '/images/notice/notice2.jpg' // 图片路径,根据实际情况修改} , // 可以添加更多社区公告数据] } , onLoad: function ( ) { // 页面加载时执行的逻辑this. refresh( ) } , refresh( ) { wx. showLoading( { mask: true} ) wx. request( { url: api. notice, method: "GET" , success: ( res) = > { this. setData( { noticeList: res. data} ) } , complete( ) { wx. hideLoading( ) } } ) }
} )
< !- - community_notice. wxml - - >
< view class = "container" > < !- - 使用wx: for 循环遍历社区公告列表 - - > < view wx: for = "{{noticeList}}" wx: key= "index" class = "notice-item" > < !- - 左侧图片 - - > < image class = "notice-image" src= "{{item.igm}}" mode= "aspectFill" > < / image> < !- - 右侧内容 - - > < view class = "notice-content" > < view class = "notice-title" > { { item. title} } < / view> < view class = "notice-time" > { { item. create_time} } < / view> < view class = "notice-details" > { { item. content} } < / view> < / view> < / view>
< / view>
/ * community_notice. wxss * /
. container { padding: 20rpx;
} . notice- item { display: flex; align- items: flex- start; margin- bottom: 20rpx; / * 添加间距 * / border- bottom: 1px solid padding- bottom: 20rpx; / * 增加底部内边距 * /
} . notice- image { width: 150rpx; height: 120rpx; border- radius: 6rpx; margin- right: 20rpx;
} . notice- content { flex: 1 ;
} . notice- title { font- size: 28rpx; font- weight: bold; margin- bottom: 10rpx;
} . notice- time { font- size: 24rpx; color: margin- bottom: 10rpx;
} . notice- details { font- size: 24rpx; color:
}
6.2 后端接口
from . models import Notice
from . serializer import NoticeSerializer
class NoticeView ( GenericViewSet, ListModelMixin) : queryset = Notice. objects. all ( ) . order_by( 'create_time' ) serializer_class = NoticeSerializer
class NoticeSerializer ( serializers. ModelSerializer) : class Meta : model = Noticefields = [ 'id' , 'title' , 'igm' , 'create_time' , 'content' ] extra_kwargs= { 'create_time' : { 'format' : "%Y-%m-%d" } }
七 活动列表
小程序端
var app = getApp( ) ;
var api = require( "../../config/settings.js" )
Page( { data: { activityList: [ ] } , onLoad: function ( ) { // 页面加载时执行的逻辑this. refresh( ) } , refresh( ) { wx. showLoading( { mask: true} ) wx. request( { url: api. activity, method: "GET" , success: ( res) = > { this. setData( { activityList: res. data} ) } , complete( ) { wx. hideLoading( ) } } ) } , handleSignup: function ( event) { // 处理报名按钮点击事件var index = event. currentTarget. dataset. index; // 获取当前点击的活动索引console. log( '点击了报名按钮,索引为:' , index) ; }
} )
< !- - activity_signup. wxml - - >
< view class = "container" > < !- - 使用wx: for 循环遍历活动报名列表 - - > < view wx: for = "{{activityList}}" wx: key= "index" class = "activity-item" > < !- - 活动内容 - - > < view class = "activity-content" > < view class = "activity-title" > { { item. title} } < / view> < view class = "activity-enrollment" > 报名人数:{ { item. count} } | 总人数:{ { item. total_count} } < / view> < view class = "activity-time" > 获得积分:{ { item. score} } < / view> < view class = "activity-time" > { { item. date} } < / view> < view class = "activity-description" > { { item. text} } < / view> < / view> < !- - 报名按钮 - - > < button class = "signup-btn" bindtap= "handleSignup" > 报名< / button> < / view>
< / view>
/ * activity_signup. wxss * /
. container { padding: 20rpx;
} . activity- item { display: flex; align- items: flex- start; justify- content: space- between; margin- bottom: 20rpx; border- bottom: 1px solid padding- bottom: 20rpx;
} . activity- content { flex: 1 ;
} . activity- title { font- size: 28rpx; font- weight: bold; margin- bottom: 10rpx;
} . activity- time { font- size: 24rpx; color: margin- bottom: 10rpx;
} . activity- enrollment { font- size: 24rpx; color: margin- bottom: 10rpx;
} . activity- description { font- size: 24rpx; color: margin- top: 10rpx; white- space: pre- wrap; / * 自动换行 * /
} . signup- btn { background- color: color: border: none; border- radius: 4rpx; padding: 10rpx 20rpx; font- size: 24rpx;
}
后端接口
from . models import Activity
from . serializer import ActivitySerializer
class ActivityView ( GenericViewSet, ListModelMixin) : queryset = Activity. objects. all ( ) . order_by( 'date' ) serializer_class = ActivitySerializer
class ActivitySerializer ( serializers. ModelSerializer) : class Meta : model = Activityfields = [ 'id' , 'title' , 'text' , 'date' , 'count' , 'score' , 'total_count' ] extra_kwargs= { 'date' : { 'format' : "%Y-%m-%d" } }
class UserInfo ( models. Model) : name = models. CharField( verbose_name= "姓名" , max_length= 32 ) avatar = models. FileField( verbose_name= "头像" , max_length= 128 , upload_to= 'avatar' ) create_date = models. DateField( verbose_name= "日期" , auto_now_add= True ) score = models. IntegerField( verbose_name= "积分" , default= 0 ) class Meta : verbose_name_plural = '用户表' def __str__ ( self) : return self. name
class Activity ( models. Model) : title = models. CharField( verbose_name= "活动标题" , max_length= 128 ) text = models. TextField( verbose_name= "活动描述" , null= True , blank= True ) date = models. DateField( verbose_name= "举办活动日期" ) count = models. IntegerField( verbose_name= '报名人数' , default= 0 ) total_count = models. IntegerField( verbose_name= '总人数' , default= 0 ) score = models. IntegerField( verbose_name= "积分" , default= 0 ) join_record = models. ManyToManyField( verbose_name= "参与者" , through= "JoinRecord" , through_fields= ( "activity" , "user" ) , to= "UserInfo" ) class Meta : verbose_name_plural = '活动表' def __str__ ( self) : return self. title
class JoinRecord ( models. Model) : user = models. ForeignKey( verbose_name= '用户' , to= "UserInfo" , on_delete= models. CASCADE) activity = models. ForeignKey( verbose_name= "活动" , to= "Activity" , on_delete= models. CASCADE, related_name= 'ac' ) exchange = models. BooleanField( verbose_name= "是否已兑换" , default= False ) class Meta : verbose_name_plural = '活动报名记录'