add user avatar

This commit is contained in:
David 2019-05-24 22:42:35 +08:00
parent 269f13a369
commit f7fbc3036b
43 changed files with 445 additions and 356 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ frontend/dist
# django
db.sqlite3
/media/*

View File

@ -25,7 +25,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'l%o*o21h_a5^f+3xblzzfch*&2k_4pcak(o#t2@vymeb_mcy3$'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = ENV == 'DEV'
DEBUG = not ENV == 'PROD'
ALLOWED_HOSTS = [] if DEBUG else ['127.0.0.1', 'localhost', 'code.wh.sdu.edu.cn']
@ -138,6 +138,10 @@ STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'frontend', 'dist', 'static'),
]
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Django Restfull Framework
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'utils.handler.custom_exception_handler',

View File

@ -14,13 +14,19 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.urls import path, include, re_path
from django.views.generic.base import TemplateView
from django.views.static import serve
from . import settings
from utils.views import UploadFileAPI
urlpatterns = [
path('', TemplateView.as_view(template_name='index.html')),
re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
path('api-auth/', include('rest_framework.urls')),
path('admin/', admin.site.urls),
path('api/user/', include('user.urls', namespace='user')),
path('api/booking/', include('booking.urls', namespace='booking')),
path('api/file/', UploadFileAPI.as_view())
]

View File

@ -0,0 +1,10 @@
from django.core.management.base import BaseCommand
from booking.models import Setting
class Command(BaseCommand):
help = 'init the settings'
def handle(self, *args, **options):
if (Setting.objects.all().count() == 0):
Setting.objects.create()
self.stdout.write(self.style.SUCCESS('Successfully init'))

View File

@ -1,7 +1,6 @@
# Generated by Django 2.1.7 on 2019-04-19 21:07
# Generated by Django 2.2.1 on 2019-05-24 20:40
import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
@ -12,7 +11,6 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
@ -26,11 +24,9 @@ class Migration(migrations.Migration):
('date', models.DateField(verbose_name='日期')),
('arrive_time', models.TimeField(blank=True, null=True, verbose_name='到达时间')),
('leave_time', models.TimeField(blank=True, null=True, verbose_name='离开时间')),
('is_valid', models.BooleanField(default=True, verbose_name='是否有效')),
('is_cancel', models.BooleanField(default=False, verbose_name='是否取消')),
('status', models.CharField(choices=[('PENDING', '正在申请'), ('SUCCESS', '申请成功'), ('FAILED', '申请失败'), ('CANCELED', '被取消')], default='PENDING', max_length=10, verbose_name='状态')),
('cancel_datetime', models.DateTimeField(blank=True, null=True, verbose_name='取消日期时间')),
('cancel_reason', models.CharField(max_length=512, verbose_name='取消理由')),
('cancel_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cancel_by', to=settings.AUTH_USER_MODEL, verbose_name='取消人')),
('cancel_reason', models.CharField(blank=True, max_length=512, null=True, verbose_name='取消理由')),
],
options={
'verbose_name': '预约',
@ -42,25 +38,13 @@ class Migration(migrations.Migration):
fields=[
('id', models.CharField(default=uuid.uuid1, max_length=36, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=128, verbose_name='名称')),
('decription', models.CharField(blank=True, max_length=512, verbose_name='说明')),
('description', models.TextField(blank=True, max_length=512, verbose_name='说明')),
],
options={
'verbose_name': '房间',
'verbose_name_plural': '房间',
},
),
migrations.CreateModel(
name='Seat',
fields=[
('id', models.CharField(default=uuid.uuid1, max_length=36, primary_key=True, serialize=False, verbose_name='ID')),
('creaeted_datetime', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间')),
],
options={
'verbose_name': '座位',
'verbose_name_plural': '座位',
},
),
migrations.CreateModel(
name='Setting',
fields=[
@ -77,24 +61,17 @@ class Migration(migrations.Migration):
'verbose_name_plural': '预约设置',
},
),
migrations.AddField(
model_name='booking',
name='room',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间'),
),
migrations.AddField(
model_name='booking',
name='seat',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='booking.Seat', verbose_name='座位'),
),
migrations.AddField(
model_name='booking',
name='seats',
field=models.ManyToManyField(related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
migrations.AddField(
model_name='booking',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL, verbose_name='预约人'),
migrations.CreateModel(
name='Seat',
fields=[
('id', models.CharField(default=uuid.uuid1, max_length=36, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=128, verbose_name='名称')),
('created_datetime', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间')),
],
options={
'verbose_name': '座位',
'verbose_name_plural': '座位',
},
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 15:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='room',
name='decription',
field=models.TextField(blank=True, max_length=512, verbose_name='说明'),
),
]

View File

@ -0,0 +1,38 @@
# Generated by Django 2.2.1 on 2019-05-24 20:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('booking', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='booking',
name='cancel_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cancel_by', to=settings.AUTH_USER_MODEL, verbose_name='取消人'),
),
migrations.AddField(
model_name='booking',
name='room',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间'),
),
migrations.AddField(
model_name='booking',
name='seats',
field=models.ManyToManyField(related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
migrations.AddField(
model_name='booking',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL, verbose_name='预约人'),
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 15:36
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('booking', '0002_auto_20190422_1505'),
]
operations = [
migrations.RemoveField(
model_name='booking',
name='is_valid',
),
]

View File

@ -1,25 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 15:56
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('booking', '0003_remove_booking_is_valid'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='cancel_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cancel_by', to=settings.AUTH_USER_MODEL, verbose_name='取消人'),
),
migrations.AlterField(
model_name='booking',
name='cancel_reason',
field=models.CharField(blank=True, max_length=512, null=True, verbose_name='取消理由'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 15:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0004_auto_20190422_1556'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='seats',
field=models.ManyToManyField(blank=True, null=True, related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 16:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('booking', '0005_auto_20190422_1556'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='room',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 16:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0006_auto_20190422_1636'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='seats',
field=models.ManyToManyField(blank=True, related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
]

View File

@ -1,22 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 16:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0007_auto_20190422_1636'),
]
operations = [
migrations.RemoveField(
model_name='booking',
name='seat',
),
migrations.AlterField(
model_name='booking',
name='seats',
field=models.ManyToManyField(related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-22 17:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('booking', '0008_auto_20190422_1649'),
]
operations = [
migrations.RenameField(
model_name='seat',
old_name='creaeted_datetime',
new_name='created_datetime',
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-26 19:47
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('booking', '0009_auto_20190422_1734'),
]
operations = [
migrations.RenameField(
model_name='room',
old_name='decription',
new_name='description',
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-27 10:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0010_auto_20190426_1947'),
]
operations = [
migrations.AddField(
model_name='seat',
name='name',
field=models.CharField(blank=True, max_length=128, verbose_name='名称'),
),
]

View File

@ -1,22 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-30 09:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0011_seat_name'),
]
operations = [
migrations.RemoveField(
model_name='booking',
name='is_cancel',
),
migrations.AddField(
model_name='booking',
name='status',
field=models.CharField(choices=[('PENDING', '正在申请'), ('SUCCESS', '申请成功'), ('FAILED', '申请失败'), ('CANCELED', '被取消')], default='PENDING', max_length=10, verbose_name='状态'),
),
]

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,10 +1,10 @@
import fetchAPI from '../utils/fetch'
export default {
getSettingDetail() {
getSettingDetail () {
return fetchAPI('booking/setting/1/', 'get')
},
updateSettingDetail(setting) {
updateSettingDetail (setting) {
return fetchAPI('booking/setting/1/', 'put', setting)
}
}

View File

@ -17,5 +17,11 @@ export default {
return fetchAPI('user/resetPassword/', 'post', {
new_password
})
},
getUserAvatar () {
return fetchAPI('user/avatar/', 'get')
},
updateUserAvatar (data) {
return fetchAPI('user/avatar/', 'post', data)
}
}

View File

@ -20,18 +20,18 @@ const user = {
},
actions: {
// 登录
Login ({ commit }, user) {
Login ({ dispatch, commit }, user) {
return new Promise((resolve, reject) => {
api.login(user.username, user.password)
.then(data => {
if (data.role !== 'ADMIN') {
if (!data.is_staff) {
notification.error({
message: '错误',
description: '不是管理员账户',
description: '不是管理员或教师账户',
key: 'ERROR'
})
this.Logout().then(() => {})
reject('Not admin account')
dispatch('Logout').then(() => {})
reject('Not a admin or teacher account')
} else {
commit('SET_ID', data.id)
commit('SET_USERNAME', data.username)
@ -43,18 +43,18 @@ const user = {
},
// 获取用户信息
GetInfo ({ commit }) {
GetInfo ({ dispatch, commit }) {
return new Promise((resolve, reject) => {
api.getUser(this.state.user.id)
.then(data => {
if (data.role !== 'ADMIN') {
if (!data.is_staff) {
notification.error({
message: '错误',
description: '不是管理员账户',
description: '不是管理员或教师账户',
key: 'ERROR'
})
this.Logout().then(() => {})
reject('Not admin account')
dispatch('Logout').then(() => {})
reject('Not a admin or teacher account')
} else {
commit('SET_ID', data.id)
commit('SET_USERNAME', data.username)

View File

@ -8,7 +8,7 @@ const BASE_URL = '/api/'
* @param name
* @returns {*}
*/
function getCookie (name) {
export function getCookie (name) {
var cookieValue = null
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';')

View File

@ -102,7 +102,7 @@
<a-form-item
label="角色"
v-bind="layout">
<a-tooltip title="只有管理员才能够进入Booking Admin" placement="right">
<a-tooltip title="只有管理员和教师才能够进入Booking Admin" placement="right">
<a-select
v-decorator="[
'role',
@ -119,44 +119,26 @@
</a-tooltip>
</a-form-item>
<a-form-item
label="职员"
label="教师"
v-bind="layout">
<a-tooltip title="标识是否能够进入Admin" placement="right">
<a-radio-group
v-decorator="[
'is_staff',
<a-select
showSearch
placeholder="搜索用户名邮箱"
:showArrow="false"
:filterOption="false"
:notFoundContent="null"
mode="multiple"
:disabled="form.getFieldValue('role') !== 'STUDENT'"
@search="handleSearch"
v-decorator="[
'teacher',
{
rules: [
{required: true, message: '请选择'},
],
validateTrigger: 'blur',
initialValue: isEdit ? account.is_staff : false
initialValue: isEdit ? account.teacher : []
}
]">
<a-radio :value="true"></a-radio>
<a-radio :value="false"></a-radio>
</a-radio-group>
</a-tooltip>
</a-form-item>
<a-form-item
label="超级管理员"
v-bind="layout">
<a-tooltip title="标识是否拥有全部权限" placement="right">
<a-radio-group
v-decorator="[
'is_superuser',
{
rules: [
{required: true, message: '请选择'},
],
validateTrigger: 'blur',
initialValue: isEdit ? account.is_superuser : false
}
]">
<a-radio :value="true"></a-radio>
<a-radio :value="false"></a-radio>
</a-radio-group>
</a-tooltip>
<a-select-option v-for="item in teacherList" :value="item.id">{{item.username}} {{item.email}}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="状态"
@ -255,6 +237,11 @@
},
form: this.$form.createForm(this),
roleList: [],
teacherList: [],
params: {
search: '',
role: 'TEACHER'
},
account: {}
}
},
@ -262,26 +249,33 @@
api.getRoleList()
.then(data => {
this.roleList = data
if (this.isEdit) this.getData()
this.getData()
if (this.isEdit) {
api.getUser(this.id)
.then(data => {
this.account = data
})
}
})
},
methods: {
getData () {
api.getUser(this.id)
api.getUserList(this.params)
.then(data => {
this.account = data
this.teacherList = data.results
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
console.log(data)
if (this.isEdit) {
api.updateUser(this.account.id, data)
.then(data => {
this.$notification.success({
message: '成功',
description: `更新用户${this.account.id}成功`,
description: `更新用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
@ -291,7 +285,7 @@
.then(data => {
this.$notification.success({
message: '成功',
description: `新建用户${this.account.id}成功`,
description: `新建用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
@ -305,7 +299,7 @@
.then(data => {
this.$notification.success({
message: '成功',
description: `删除用户${this.account.id}成功`,
description: `删除用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
@ -343,6 +337,10 @@
}
callback()
return true
},
handleSearch (value) {
this.params.search = value
this.getData()
}
},
computed: {

View File

@ -49,6 +49,13 @@
</a-spin>
</div>
<a-row class="btn-row">
<div style="float: left;">
<span style="vertical-align: -6px;">提示:
<span class="box white"></span>可选时间
<span class="box green"></span>不可选时间
<span class="box blue"></span>已选时间
</span>
</div>
<a-button
style="float: right"
type="primary"
@ -242,5 +249,26 @@
.btn-row {
margin: 10px 0;
.box {
display: inline-block;
margin: 0 2px 0 8px;
vertical-align: -2px;
height: 14px;
width: 14px;
border: solid 1px rgba(128, 128, 128, 0.3);
}
.green {
background-color: @success-color;
}
.blue {
background-color: @primary-color;
}
.white {
background-color: #fff;
}
}
</style>

View File

@ -8,7 +8,23 @@
<a-form-item
label="头像"
v-bind="layout">
TODO
<a-upload
name="file"
action="/api/file/"
:withCredentials="true"
:headers="headers"
accept="image/*"
listType="picture-card"
:showUploadList="false"
:beforeUpload="beforeUpload"
@change="handleImageChange">
<img v-if="image.url" :src="image.url" alt="头像" width="200px"/>
<div v-else style="width: 100px">
<a-icon :type="status.image.loading ? 'loading' : 'plus'"></a-icon>
<div class="text">选择图片</div>
</div>
</a-upload>
<span>支持图片文件不大于10MB</span>
</a-form-item>
</a-form>
<a-divider>重置密码</a-divider>
@ -58,6 +74,7 @@
<script>
import api from '../../api/user'
import { getCookie } from '../../utils/fetch'
export default {
name: 'UserSetting',
@ -69,11 +86,26 @@
},
reset: {
form: this.$form.createForm(this),
},
status: {
image: {
loading: false
}
},
image: {
url: '',
id: ''
},
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
}
},
mounted () {
api.getUserAvatar()
.then(data => {
this.image = data
})
},
methods: {
handleSubmit (e) {
@ -107,7 +139,38 @@
}
callback()
return true
}
},
handleImageChange (info) {
if (info.file.status === 'uploading') {
this.loading = true
} else if (info.file.status === 'done') {
this.image.id = info.file.response.id
this.image.url = info.file.response.url.toString()
this.loading = false
api.updateUserAvatar({ id: this.image.id })
.then(data => {
this.$notification.success({ message: '成功', description: '上传头像成功', key: 'SUCCESS' })
})
} else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} 上传失败.`)
this.loading = false
}
},
beforeUpload (file) {
const isImage = /image\/*/.test(file.type)
if (!isImage) {
this.$message.error('图片格式不正确')
}
const isLt10M = file.size / 1024 / 1024 <= 10
if (!isLt10M) {
this.$message.error('图片大小请小于10MB')
}
const isNameLt256 = file.name.length < 256
if (!isNameLt256) {
this.$message.error('图片名称请小于256个字符')
}
return isImage && isLt10M && isNameLt256
},
}
}

View File

@ -59,7 +59,11 @@ module.exports = {
devServer: {
proxy: {
'/api': {
// target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
target: 'http://localhost:8000',
ws: false,
changeOrigin: true
},
'/media': {
target: 'http://localhost:8000',
ws: false,
changeOrigin: true

View File

@ -1,8 +1,10 @@
# Generated by Django 2.1.7 on 2019-04-19 21:07
# Generated by Django 2.2.1 on 2019-05-24 20:40
from django.conf import settings
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
@ -11,7 +13,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
('auth', '0011_update_proxy_permissions'),
('utils', '0001_initial'),
]
operations = [
@ -27,9 +30,11 @@ class Migration(migrations.Migration):
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('role', models.CharField(choices=[('TEACHER', '老师'), ('LEADER', '队长'), ('MEMBER', '')], max_length=10, verbose_name='角色')),
('role', models.CharField(choices=[('TEACHER', '教师'), ('STUDENT', '学生'), ('ADMIN', '管理')], max_length=10, verbose_name='角色')),
('point', models.IntegerField(default=20, verbose_name='积分')),
('avatar', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='utils.UploadFile', verbose_name='头像')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('teacher', models.ManyToManyField(blank=True, related_name='_user_teacher_+', to=settings.AUTH_USER_MODEL, verbose_name='教师')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={

View File

@ -1,18 +0,0 @@
# Generated by Django 2.1.7 on 2019-04-26 19:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('user', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='role',
field=models.CharField(choices=[('TEACHER', '老师'), ('LEADER', '队长'), ('MEMBER', '成员'), ('ADMIN', '管理员')], max_length=10, verbose_name='角色'),
),
]

View File

@ -1,15 +1,16 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from utils.models import UploadFile
class User(AbstractUser):
ROLE_CHOICE = [
('TEACHER', '老师'),
('LEADER', '队长'),
('MEMBER', '成员'),
('TEACHER', '教师'),
('STUDENT', '学生'),
('ADMIN', '管理员'),
]
role = models.CharField('角色', max_length=10, choices=ROLE_CHOICE)
teacher = models.ManyToManyField(verbose_name='教师', to='self', related_name='students', blank=True)
first_name = None
last_name = None
point = models.IntegerField('积分', default=20)
avatar = models.ForeignKey(verbose_name='头像', to=UploadFile, on_delete=models.CASCADE, null=True)

View File

@ -8,7 +8,8 @@ class UserSerializer(serializers.ModelSerializer):
model = models.User
exclude = ('groups', 'user_permissions')
extra_kwargs = {
'password': {'write_only': True, 'required': False}
'password': {'write_only': True, 'required': False},
'teacher': {'required': False}
}

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -11,5 +11,6 @@ urlpatterns = [
path('logout/', views.Logout.as_view()),
path('getInfo/', views.GetInfo.as_view()),
path('resetPassword/', views.ResetPassword.as_view()),
path('checkUsername/', views.CheckUsername.as_view())
path('checkUsername/', views.CheckUsername.as_view()),
path('avatar/', views.Avatar.as_view()),
]

View File

@ -1,7 +1,7 @@
from django.contrib import auth
from django.db.models import QuerySet
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from django.shortcuts import get_object_or_404, get_list_or_404
from rest_framework import generics
from rest_framework import permissions
from rest_framework import views
@ -14,6 +14,7 @@ from rest_framework.serializers import Serializer
from . import models
from . import serializers
from utils.models import UploadFile
class User(generics.ListCreateAPIView):
"""
@ -42,7 +43,27 @@ class User(generics.ListCreateAPIView):
def perform_create(self, serializer):
user = serializer.validated_data
models.User.objects.create_user(**user)
role = user.get('role')
is_staff = False
is_superuser = False
if role == 'TEACHER':
is_staff = True
elif role == 'ADMIN':
is_staff = True
is_superuser = True
u = models.User.objects.create_user(
username=user['username'],
password=user['password'],
role=user['role'],
email=user['email'],
is_staff=is_staff,
is_superuser=is_superuser,
is_active=user['is_active'],
point=user['point']
)
teacher_list = user.get('teacher')
for teacher in teacher_list:
u.teacher.add(teacher)
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
@ -59,10 +80,23 @@ class UserDetail(generics.RetrieveUpdateDestroyAPIView):
serializer.save()
data = serializer.validated_data
password = data.get('password')
user = get_object_or_404(models.User, id=self.kwargs['pk'])
if password:
user = get_object_or_404(models.User, id=self.kwargs['pk'])
user.set_password(password)
user.save()
role = user.role
if role != 'STUDENT':
user.teacher.clear()
is_staff = False
is_superuser = False
if role == 'TEACHER':
is_staff = True
elif role == 'ADMIN':
is_staff = True
is_superuser = True
user.is_staff = is_staff
user.is_superuser = is_superuser
user.save()
class RoleList(generics.GenericAPIView):
@ -155,3 +189,25 @@ class CheckUsername(views.APIView):
return Response(models.User.objects.filter(username=username).exists())
else:
raise ValidationError("Did not get username.")
class Avatar(views.APIView):
"""
获得更新用户头像
"""
permission_classes = (permissions.IsAuthenticated,)
def get(self, request):
avatar = request.user.avatar
return Response({
'id': avatar.id if avatar else None,
'url': avatar.get_url() if avatar else None
})
def post(self, request):
file_id = request.data.get('id')
file = get_object_or_404(UploadFile, id=file_id)
user = request.user
user.avatar = file
user.save()
return Response(serializers.UserSerializer(user).data)

View File

@ -0,0 +1 @@
default_app_config = 'utils.apps.UtilsConfig'

View File

@ -1,3 +1,8 @@
from django.contrib import admin
# Register your models here.
from . import models
@admin.register(models.UploadFile)
class UploadFileAdmin(admin.ModelAdmin):
pass

View File

@ -3,3 +3,4 @@ from django.apps import AppConfig
class UtilsConfig(AppConfig):
name = 'utils'
verbose_name = '其他'

View File

@ -0,0 +1,27 @@
# Generated by Django 2.2.1 on 2019-05-24 20:37
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='UploadFile',
fields=[
('datetime', models.DateTimeField(auto_created=True, verbose_name='上传日期')),
('id', models.UUIDField(default=uuid.uuid1, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(max_length=1024, upload_to='media', verbose_name='文件')),
],
options={
'verbose_name': '上传文件',
'verbose_name_plural': '上传文件',
},
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.1 on 2019-05-24 21:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('utils', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='uploadfile',
name='name',
field=models.CharField(default='', max_length=256, verbose_name='文件名'),
preserve_default=False,
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.1 on 2019-05-24 21:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('utils', '0002_uploadfile_name'),
]
operations = [
migrations.AlterField(
model_name='uploadfile',
name='datetime',
field=models.DateTimeField(auto_now_add=True, verbose_name='上传日期'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.1 on 2019-05-24 21:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('utils', '0003_auto_20190524_2119'),
]
operations = [
migrations.AlterField(
model_name='uploadfile',
name='file',
field=models.FileField(max_length=1024, upload_to='file', verbose_name='文件'),
),
]

View File

@ -1,3 +1,24 @@
from django.db import models
import uuid
# Create your models here.
from django.db import models
from BookingService import settings
class UploadFile(models.Model):
id = models.UUIDField('ID', default=uuid.uuid1, primary_key=True)
name = models.CharField('文件名', max_length=256)
file = models.FileField('文件', upload_to='file', max_length=1024)
datetime = models.DateTimeField('上传日期', auto_now_add=True)
class Meta:
verbose_name = '上传文件'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
def delete(self, using=None, keep_parents=False):
self.file.delete()
super(UploadFile, self).delete()
def get_url(self):
return f'{settings.MEDIA_URL}{self.file}'

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,3 +1,23 @@
from django.shortcuts import render
import time
from rest_framework import permissions
from rest_framework import views
from rest_framework.response import Response
# Create your views here.
from utils.models import UploadFile
class UploadFileAPI(views.APIView):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request):
file = request.FILES['file']
filename = file.name
file.name = f'{int(time.time())}_{filename}'
f = UploadFile.objects.create(
name=filename,
file=file,
)
return Response({
'id': f.id,
'url': f.get_url()
})