init
This commit is contained in:
commit
8c26dcb169
BIN
db.sqlite3
Normal file
BIN
db.sqlite3
Normal file
Binary file not shown.
1
exam/__init__.py
Normal file
1
exam/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
default_app_config = 'exam.apps.ExamConfig'
|
||||
8
exam/admin.py
Normal file
8
exam/admin.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
from .models import Question, Choice, CyberUser
|
||||
|
||||
admin.site.register(Question)
|
||||
admin.site.register(Choice)
|
||||
admin.site.register(CyberUser)
|
||||
6
exam/apps.py
Normal file
6
exam/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ExamConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'exam'
|
||||
129
exam/form.py
Normal file
129
exam/form.py
Normal file
@ -0,0 +1,129 @@
|
||||
from django.forms import Form, fields
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
class RegisterForm(Form):
|
||||
username = fields.CharField(
|
||||
required=True,
|
||||
min_length=2,
|
||||
max_length=20,
|
||||
error_messages={
|
||||
'required': '用户名不可以为空!',
|
||||
'min_length': '用户名应大于2位!',
|
||||
'max_length': '用户名应小于20位!',
|
||||
},
|
||||
)
|
||||
|
||||
nickname = fields.CharField(
|
||||
required=True,
|
||||
min_length=2,
|
||||
max_length=20,
|
||||
error_messages={
|
||||
'required': '昵称不可以为空!',
|
||||
'min_length': '昵称应大于2位!',
|
||||
'max_length': '昵称应小于20位!',
|
||||
},
|
||||
)
|
||||
|
||||
password1 = fields.CharField(
|
||||
required=True,
|
||||
min_length=6,
|
||||
max_length=20,
|
||||
error_messages={
|
||||
'required': '密码不可以位空!',
|
||||
'min_length': '密码应大于6位!',
|
||||
'max_length': '密码应小于20位!',
|
||||
},
|
||||
)
|
||||
|
||||
password2 = fields.CharField(
|
||||
required=True,
|
||||
error_messages={
|
||||
'required': '请再次输入密码!',
|
||||
},
|
||||
)
|
||||
|
||||
email = fields.EmailField(
|
||||
required=True,
|
||||
error_messages={
|
||||
'required': '请随便填个邮箱吧...',
|
||||
},
|
||||
)
|
||||
|
||||
def clean_password2(self):
|
||||
if not self.errors.get("password1"):
|
||||
if self.cleaned_data["password2"] != self.cleaned_data["password1"]:
|
||||
raise ValidationError("您输入的密码不一致,请重新输入!")
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class LoginForm(Form):
|
||||
username = fields.CharField(
|
||||
required=True,
|
||||
error_messages={
|
||||
'required': '请输入用户名',
|
||||
},
|
||||
)
|
||||
|
||||
password = fields.CharField(
|
||||
required=True,
|
||||
error_messages={
|
||||
'required': '请输入密码',
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class CreateForm(Form):
|
||||
text = fields.CharField(
|
||||
required=True,
|
||||
max_length=300,
|
||||
error_messages={
|
||||
'required': '请输入题目',
|
||||
'max_length': '不得超过300字'
|
||||
}
|
||||
)
|
||||
|
||||
image = fields.ImageField(required=False)
|
||||
|
||||
a = fields.CharField(
|
||||
required=True,
|
||||
max_length=300,
|
||||
error_messages={
|
||||
'required': '请输入选项',
|
||||
'max_length': '不得超过300字'
|
||||
}
|
||||
)
|
||||
|
||||
b = fields.CharField(
|
||||
required=True,
|
||||
max_length=300,
|
||||
error_messages={
|
||||
'required': '请输入选项',
|
||||
'max_length': '不得超过300字'
|
||||
}
|
||||
)
|
||||
|
||||
c = fields.CharField(
|
||||
required=True,
|
||||
max_length=300,
|
||||
error_messages={
|
||||
'required': '请输入选项',
|
||||
'max_length': '不得超过300字'
|
||||
}
|
||||
)
|
||||
|
||||
d = fields.CharField(
|
||||
required=True,
|
||||
max_length=300,
|
||||
error_messages={
|
||||
'required': '请输入选项',
|
||||
'max_length': '不得超过300字'
|
||||
}
|
||||
)
|
||||
|
||||
answer = fields.ChoiceField(choices=(('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D')))
|
||||
|
||||
|
||||
class AnswerForm(Form):
|
||||
questionID = fields.UUIDField()
|
||||
answer = fields.ChoiceField(choices=(('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D')))
|
||||
70
exam/migrations/0001_initial.py
Normal file
70
exam/migrations/0001_initial.py
Normal file
@ -0,0 +1,70 @@
|
||||
# Generated by Django 5.0.7 on 2024-07-11 05:50
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Question',
|
||||
fields=[
|
||||
('uid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, unique=True)),
|
||||
('question_text', models.CharField(max_length=300, unique=True, verbose_name='题目文本')),
|
||||
('pub_date', models.DateTimeField(auto_now=True, verbose_name='发布日期')),
|
||||
('image', models.ImageField(blank=True, null=True, upload_to='images/question_images/', verbose_name='图片')),
|
||||
('choice_a', models.CharField(max_length=300)),
|
||||
('choice_b', models.CharField(max_length=300)),
|
||||
('choice_c', models.CharField(max_length=300)),
|
||||
('choice_d', models.CharField(max_length=300)),
|
||||
('answer', models.CharField(choices=[('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D')], max_length=10)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '问题',
|
||||
'verbose_name_plural': '问题',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CyberUser',
|
||||
fields=[
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('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')),
|
||||
('uid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('username', models.CharField(max_length=15, unique=True, verbose_name='用户名')),
|
||||
('nickname', models.CharField(blank=True, max_length=13, null=True, verbose_name='昵称')),
|
||||
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='邮箱')),
|
||||
('picture', models.ImageField(blank=True, null=True, upload_to='images/avatars', verbose_name='用户头像')),
|
||||
('date_joined', models.DateTimeField(auto_now_add=True)),
|
||||
('score', models.IntegerField(default=0)),
|
||||
('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')),
|
||||
('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={
|
||||
'verbose_name': '用户',
|
||||
'verbose_name_plural': '用户',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Choice',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('choice', models.CharField(choices=[('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D')], max_length=10)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)),
|
||||
('question', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='exam.question')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
exam/migrations/__init__.py
Normal file
0
exam/migrations/__init__.py
Normal file
81
exam/models.py
Normal file
81
exam/models.py
Normal file
@ -0,0 +1,81 @@
|
||||
import uuid
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import BaseUserManager, AbstractUser
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class Question(models.Model):
|
||||
uid = models.UUIDField(primary_key=True, default=uuid.uuid4, unique=True)
|
||||
question_text = models.CharField(max_length=300, unique=True, verbose_name='题目文本')
|
||||
pub_date = models.DateTimeField(auto_now=True, verbose_name='发布日期')
|
||||
|
||||
image = models.ImageField(upload_to='images/question_images/', verbose_name='图片', blank=True, null=True)
|
||||
|
||||
choice_a = models.CharField(max_length=300)
|
||||
choice_b = models.CharField(max_length=300)
|
||||
choice_c = models.CharField(max_length=300)
|
||||
choice_d = models.CharField(max_length=300)
|
||||
|
||||
answer = models.CharField(choices=(('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D')), max_length=10)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '问题'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
class UserManager(BaseUserManager): # 自定义Manager管理器
|
||||
def _create_user(self, username, password, email, **kwargs):
|
||||
if not username:
|
||||
raise ValueError("请传入用户名!")
|
||||
if not password:
|
||||
raise ValueError("请传入密码!")
|
||||
if not email:
|
||||
raise ValueError("请传入邮箱地址!")
|
||||
user = self.model(username=username, email=email, **kwargs)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def create_user(self, username, password, email, **kwargs): # 创建普通用户
|
||||
kwargs['is_superuser'] = False
|
||||
return self._create_user(username, password, email, **kwargs)
|
||||
|
||||
def create_superuser(self, username, password, email, **kwargs): # 创建超级用户
|
||||
kwargs['is_superuser'] = True
|
||||
kwargs['is_staff'] = True
|
||||
return self._create_user(username, password, email, **kwargs)
|
||||
|
||||
|
||||
class CyberUser(AbstractUser): # 自定义User
|
||||
uid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
username = models.CharField(max_length=15, verbose_name="用户名", unique=True)
|
||||
nickname = models.CharField(max_length=13, verbose_name="昵称", null=True, blank=True)
|
||||
email = models.EmailField(verbose_name="邮箱", null=True, blank=True)
|
||||
picture = models.ImageField(upload_to="images/avatars", verbose_name="用户头像", null=True, blank=True)
|
||||
date_joined = models.DateTimeField(auto_now_add=True)
|
||||
score = models.IntegerField(default=0)
|
||||
|
||||
USERNAME_FIELD = 'username' # 使用authenticate验证时使用的验证字段,可以换成其他字段,但验证字段必须是唯一的,即设置了unique=True
|
||||
REQUIRED_FIELDS = ['email'] # 创建用户时必须填写的字段,除了该列表里的字段还包括password字段以及USERNAME_FIELD中的字段
|
||||
EMAIL_FIELD = 'email' # 发送邮件时使用的字段
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
def get_full_name(self):
|
||||
return self.username
|
||||
|
||||
def get_short_name(self):
|
||||
return self.username
|
||||
|
||||
class Meta:
|
||||
verbose_name = "用户"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
class Choice(models.Model):
|
||||
choice = models.CharField(choices=(('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D')), max_length=10)
|
||||
question = models.ForeignKey(Question, on_delete=models.DO_NOTHING)
|
||||
user = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING)
|
||||
3
exam/tests.py
Normal file
3
exam/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
12
exam/urls.py
Normal file
12
exam/urls.py
Normal file
@ -0,0 +1,12 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = (
|
||||
path("", views.home, name="index"),
|
||||
path("register/", views.register, name="register"),
|
||||
path("login/", views.login_view, name="login"),
|
||||
path("logout/", views.logout_view, name="logout"),
|
||||
path("create/", views.create_question, name="create"),
|
||||
path("question/", views.get_question, name="get_question"),
|
||||
path("answer/", views.answer, name="answer")
|
||||
)
|
||||
201
exam/views.py
Normal file
201
exam/views.py
Normal file
@ -0,0 +1,201 @@
|
||||
import uuid
|
||||
from django.core.files import File
|
||||
from django.http import JsonResponse, Http404
|
||||
from django.shortcuts import render, redirect
|
||||
from .form import RegisterForm, LoginForm, CreateForm, AnswerForm
|
||||
from .models import CyberUser, Question, Choice
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
|
||||
# Create your views here.
|
||||
|
||||
def home(request):
|
||||
content = {'user': request.user}
|
||||
return render(request, 'home.html', content)
|
||||
|
||||
|
||||
def register(request):
|
||||
if request.method == 'GET':
|
||||
return render(request, 'register.html', {'form': RegisterForm()})
|
||||
elif request.method == 'POST':
|
||||
form = RegisterForm(request.POST)
|
||||
if form.is_valid():
|
||||
username = form.cleaned_data['username']
|
||||
nickname = form.cleaned_data['nickname']
|
||||
password = form.cleaned_data['password1']
|
||||
email = form.cleaned_data['email']
|
||||
username_exist = CyberUser.objects.filter(username=username).exists()
|
||||
if username_exist:
|
||||
return JsonResponse({"code": 400,
|
||||
"message": "验证失败",
|
||||
"data": {"username": "",
|
||||
"password1": "",
|
||||
"password2": "",
|
||||
"email": "",
|
||||
},
|
||||
})
|
||||
CyberUser.objects.create_user(
|
||||
username=username,
|
||||
nickname=nickname,
|
||||
password=password,
|
||||
email=email,
|
||||
)
|
||||
return JsonResponse({"code": 200,
|
||||
"message": "验证通过,已注册",
|
||||
"data": {"username": "",
|
||||
"password1": "",
|
||||
"password2": "",
|
||||
"email": "",
|
||||
},
|
||||
})
|
||||
else:
|
||||
return JsonResponse({"code": 400, "message": "验证失败", "data": {"username": form.errors.get("username"),
|
||||
"password1": form.errors.get("password1"),
|
||||
"password2": form.errors.get("password2"),
|
||||
"email": form.errors.get("email")}})
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
|
||||
def login_view(request):
|
||||
if request.method == 'GET':
|
||||
return render(request, 'login.html', {'form': LoginForm()})
|
||||
elif request.method == 'POST':
|
||||
form = LoginForm(request.POST)
|
||||
if form.is_valid():
|
||||
username = form.cleaned_data['username']
|
||||
password = form.cleaned_data['password']
|
||||
user = authenticate(request, username=username, password=password)
|
||||
if user and user.is_active:
|
||||
login(request, user)
|
||||
request.session['username'] = username
|
||||
request.session.set_expiry(1800)
|
||||
return JsonResponse(
|
||||
{
|
||||
"code": 200,
|
||||
"message": '登陆成功',
|
||||
"data": username,
|
||||
}
|
||||
)
|
||||
elif user and user.is_active:
|
||||
return JsonResponse(
|
||||
{
|
||||
"code": 400,
|
||||
"message": "用户状态错误,请联系管理员修正",
|
||||
"data": "user is not active",
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JsonResponse(
|
||||
{
|
||||
"code": 400,
|
||||
"message": "用户名或密码错误",
|
||||
"data": None,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JsonResponse(
|
||||
{
|
||||
"code": 400,
|
||||
"message": "验证失败,用户名或密码格式错误",
|
||||
"data": form.errors.get(),
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
|
||||
def logout_view(request):
|
||||
logout(request)
|
||||
return redirect('/exam/login')
|
||||
|
||||
|
||||
@login_required
|
||||
def create_question(request):
|
||||
if request.method == 'GET':
|
||||
return render(request, 'create.html', {'form': CreateForm})
|
||||
elif request.method == 'POST':
|
||||
form = CreateForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
text = form.cleaned_data['text']
|
||||
image = form.cleaned_data['image']
|
||||
a = form.cleaned_data['a']
|
||||
b = form.cleaned_data['b']
|
||||
c = form.cleaned_data['c']
|
||||
d = form.cleaned_data['d']
|
||||
answer = form.cleaned_data['answer']
|
||||
new_question = Question(
|
||||
question_text=text,
|
||||
choice_a=a,
|
||||
choice_b=b,
|
||||
choice_c=c,
|
||||
choice_d=d,
|
||||
answer=answer,
|
||||
)
|
||||
if image:
|
||||
new_question.image = File(image, name=f'{str(uuid.uuid4())}.jpg')
|
||||
new_question.save()
|
||||
return JsonResponse(
|
||||
{
|
||||
'code': 200,
|
||||
'message': 'success',
|
||||
'data': '',
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JsonResponse(
|
||||
{
|
||||
'code': 400,
|
||||
'message': '参数不正确',
|
||||
'data': form.errors
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def get_question(request):
|
||||
user = request.user
|
||||
question = Question.objects.exclude(choice__user=user).order_by('?').first()
|
||||
return render(request, "question.html", {'question': question, 'form': AnswerForm, 'user': request.user})
|
||||
|
||||
|
||||
@login_required
|
||||
def answer(request):
|
||||
if request.method == 'POST':
|
||||
form = AnswerForm(request.POST)
|
||||
if form.is_valid():
|
||||
user = request.user
|
||||
user_ans = form.cleaned_data['answer']
|
||||
questionID = form.cleaned_data['questionID']
|
||||
question = Question.objects.get(uid=questionID)
|
||||
if Choice.objects.filter(user=user, question=question).exists():
|
||||
return JsonResponse(
|
||||
{
|
||||
'code': 200,
|
||||
'message': '该问题已经回答过了',
|
||||
'data': '',
|
||||
}
|
||||
)
|
||||
choice = Choice(user=user, question=question, choice=user_ans)
|
||||
choice.save()
|
||||
if user_ans == question.answer:
|
||||
user.score = user.score + 1
|
||||
user.save()
|
||||
return JsonResponse(
|
||||
{
|
||||
'code': 200,
|
||||
'message': 'success',
|
||||
'data': {'correction': user_ans == question.answer},
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JsonResponse(
|
||||
{
|
||||
'code': 400,
|
||||
'message': '参数错误',
|
||||
'data': '表单不可用',
|
||||
}
|
||||
)
|
||||
else:
|
||||
return Http404
|
||||
22
manage.py
Normal file
22
manage.py
Normal file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '不夜城考试.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
BIN
media/images/question_images/gawr-gura-red.jpeg
Normal file
BIN
media/images/question_images/gawr-gura-red.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 KiB |
2
static/js/jquery-3.6.0.min.js
vendored
Normal file
2
static/js/jquery-3.6.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
17
templates/base.html
Normal file
17
templates/base.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
{% block script %}{% endblock script %}
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
{% block body %}{% endblock body %}
|
||||
</body>
|
||||
|
||||
<script src={% static 'js/jquery-3.6.0.min.js' %}></script>
|
||||
|
||||
</html>
|
||||
80
templates/create.html
Normal file
80
templates/create.html
Normal file
@ -0,0 +1,80 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block tittle %}登陆{% endblock tittle %}
|
||||
{% block body %}
|
||||
<h1>创建题目</h1>
|
||||
<form id="loginForm" onsubmit="event.preventDefault(); submitForm();" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<label for="text">题目文本:</label>
|
||||
<input type="text" id="text" name="text" required><br>
|
||||
<input type="file" name="image" id="image" accept="image/jpg" onchange="imgChange(this);"> <!--文件上传选择按钮-->
|
||||
<img id="image-preview" width="50%" src=""/><br>
|
||||
<label for="A">A选项:</label>
|
||||
<input type="text" id="A" name="a" required><br>
|
||||
<label for="B">B选项:</label>
|
||||
<input type="text" id="B" name="b" required><br>
|
||||
<label for="C">C选项:</label>
|
||||
<input type="text" id="C" name="c" required><br>
|
||||
<label for="D">D选项:</label>
|
||||
<input type="text" id="D" name="d" required><br>
|
||||
|
||||
<label>答案:</label><br>
|
||||
<input type="radio" id="A" name="answer" value="A" checked>
|
||||
<label for="A">A</label><br>
|
||||
<input type="radio" id="B" name="answer" value="B">
|
||||
<label for="B">B</label><br>
|
||||
<input type="radio" id="C" name="answer" value="C">
|
||||
<label for="C">C</label><br>
|
||||
<input type="radio" id="D" name="answer" value="D">
|
||||
<label for="D">D</label><br>
|
||||
|
||||
<button type="submit">提交题目</button>
|
||||
</form>
|
||||
|
||||
<!-- 错误信息显示区域 -->
|
||||
<div id="errorMessage" style="display: none;"></div>
|
||||
{% endblock body %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
|
||||
function imgChange(obj) {
|
||||
var file = document.getElementById("image");
|
||||
var imgUrl = window.URL.createObjectURL(file.files[0]);
|
||||
var img = document.getElementById('image-preview');
|
||||
img.setAttribute('src', imgUrl); // 修改img标签src属性值
|
||||
};
|
||||
|
||||
function submitForm() {
|
||||
var formData = new FormData($('#loginForm')[0]);
|
||||
$.ajax({
|
||||
url: "{% url 'exam:create' %}", // Django模板标签,用于生成URL
|
||||
type: "POST",
|
||||
data: formData,
|
||||
contentType: false, // 告诉jQuery不要设置Content-Type请求头
|
||||
processData: false, // 告诉jQuery不要处理发送的数据
|
||||
success: function(response) {
|
||||
if (response.code === 200) {
|
||||
alert(response.message); // 成功时的消息框
|
||||
} else {
|
||||
alert(response.message); // 显示错误信息
|
||||
displayErrors(response.data);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert("An error occurred while processing your request.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayErrors(errors) {
|
||||
$('#errorMessage').html('');
|
||||
for (let key in errors) {
|
||||
if (errors.hasOwnProperty(key)) {
|
||||
let message = errors[key];
|
||||
$('#errorMessage').append('<p>' + message + '</p>');
|
||||
}
|
||||
}
|
||||
$('#errorMessage').show();
|
||||
}
|
||||
</script>
|
||||
{% endblock script %}
|
||||
14
templates/home.html
Normal file
14
templates/home.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block tittle %}不夜城考试中心{% endblock tittle %}
|
||||
{% block body %}
|
||||
<h1>不夜城考试中心</h1>
|
||||
{% if user.is_active %}
|
||||
<p>欢迎,{{ user.username }}</p>
|
||||
<a href="/exam/logout">退出</a>
|
||||
<a href="/exam/question">开始答题</a>
|
||||
{% else %}
|
||||
<a href="/exam/login">登陆</a>
|
||||
<a href="/exam/register">注册</a>
|
||||
{% endif %}
|
||||
{% endblock body %}
|
||||
|
||||
51
templates/login.html
Normal file
51
templates/login.html
Normal file
@ -0,0 +1,51 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block tittle %}登陆{% endblock tittle %}
|
||||
{% block body %}
|
||||
<h1>登陆</h1>
|
||||
<form id="loginForm" onsubmit="event.preventDefault(); submitForm();">
|
||||
{% csrf_token %}
|
||||
<label for="username">用户名:</label>
|
||||
<input type="text" id="username" name="username" required><br>
|
||||
<label for="password">密码:</label>
|
||||
<input type="password" id="password" name="password" required><br>
|
||||
<button type="submit">登陆</button>
|
||||
<a href="/exam/register">注册</a>
|
||||
</form>
|
||||
|
||||
<!-- 错误信息显示区域 -->
|
||||
<div id="errorMessage" style="display: none;"></div>
|
||||
{% endblock body %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
function submitForm() {
|
||||
$.ajax({
|
||||
url: "{% url 'exam:login' %}", // Django模板标签,用于生成URL
|
||||
type: "POST",
|
||||
data: $('#loginForm').serialize(),
|
||||
success: function(response) {
|
||||
if (response.code === 200) {
|
||||
alert(response.message); // 成功时的消息框
|
||||
window.location.href = "/exam";
|
||||
} else {
|
||||
alert(response.message); // 显示错误信息
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert("An error occurred while processing your request.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayErrors(errors) {
|
||||
$('#errorMessage').html('');
|
||||
for (let key in errors) {
|
||||
if (errors.hasOwnProperty(key)) {
|
||||
let message = errors[key];
|
||||
$('#errorMessage').append('<p>' + message + '</p>');
|
||||
}
|
||||
}
|
||||
$('#errorMessage').show();
|
||||
}
|
||||
</script>
|
||||
{% endblock script %}
|
||||
78
templates/question.html
Normal file
78
templates/question.html
Normal file
@ -0,0 +1,78 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block tittle %}随机题目{% endblock tittle %}
|
||||
{% block body %}
|
||||
<h1>题目</h1>
|
||||
{% if question %}
|
||||
<form id="loginForm" onsubmit="event.preventDefault(); submitForm();" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="questionID" value="{{ question.uid }}">
|
||||
<h2>题目文本:</h2>
|
||||
<p>{{ question.question_text }}</p><br>
|
||||
{% if question.image %}
|
||||
<img id="image-preview" width="50%" src="{{ question.image.url }}"/><br>
|
||||
{% endif %}
|
||||
<label>选择:</label><br>
|
||||
<input type="radio" id="A" name="answer" value="A" checked>
|
||||
<label for="A">A</label><br>
|
||||
<input type="radio" id="B" name="answer" value="B">
|
||||
<label for="B">B</label><br>
|
||||
<input type="radio" id="C" name="answer" value="C">
|
||||
<label for="C">C</label><br>
|
||||
<input type="radio" id="D" name="answer" value="D">
|
||||
<label for="D">D</label><br>
|
||||
|
||||
<button type="submit">提交题目</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>没有提穆啦</p>
|
||||
<h3>你的分数是:{{ user.score }}</h3>
|
||||
{% endif %}
|
||||
|
||||
<!-- 错误信息显示区域 -->
|
||||
<div id="errorMessage" style="display: none;"></div>
|
||||
{% endblock body %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
|
||||
function imgChange(obj) {
|
||||
var file = document.getElementById("image");
|
||||
var imgUrl = window.URL.createObjectURL(file.files[0]);
|
||||
var img = document.getElementById('image-preview');
|
||||
img.setAttribute('src', imgUrl); // 修改img标签src属性值
|
||||
};
|
||||
|
||||
function submitForm() {
|
||||
var formData = new FormData($('#loginForm')[0]);
|
||||
$.ajax({
|
||||
url: "{% url 'exam:answer' %}", // Django模板标签,用于生成URL
|
||||
type: "POST",
|
||||
data: formData,
|
||||
contentType: false, // 告诉jQuery不要设置Content-Type请求头
|
||||
processData: false, // 告诉jQuery不要处理发送的数据
|
||||
success: function(response) {
|
||||
if (response.code === 200) {
|
||||
alert(response.message); // 成功时的消息框
|
||||
} else {
|
||||
alert(response.message); // 显示错误信息
|
||||
displayErrors(response.data);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert("An error occurred while processing your request.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayErrors(errors) {
|
||||
$('#errorMessage').html('');
|
||||
for (let key in errors) {
|
||||
if (errors.hasOwnProperty(key)) {
|
||||
let message = errors[key];
|
||||
$('#errorMessage').append('<p>' + message + '</p>');
|
||||
}
|
||||
}
|
||||
$('#errorMessage').show();
|
||||
}
|
||||
</script>
|
||||
{% endblock script %}
|
||||
56
templates/register.html
Normal file
56
templates/register.html
Normal file
@ -0,0 +1,56 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block tittle %}注册页面{% endblock tittle %}
|
||||
{% block body %}
|
||||
<h1>注册</h1>
|
||||
<form id="registerForm" onsubmit="event.preventDefault(); submitForm();">
|
||||
{% csrf_token %}
|
||||
<label for="username">用户名:</label>
|
||||
<input type="text" id="username" name="username" required><br>
|
||||
<label for="nickname">昵称:</label>
|
||||
<input type="text" id="nickname" name="nickname" required><br>
|
||||
<label for="password1">密码:</label>
|
||||
<input type="password" id="password1" name="password1" required><br>
|
||||
<label for="password2">确认密码:</label>
|
||||
<input type="password" id="password2" name="password2" required><br>
|
||||
<label for="email">邮箱:</label>
|
||||
<input type="email" id="email" name="email" required><br>
|
||||
<button type="submit">Register</button>
|
||||
</form>
|
||||
|
||||
<!-- 错误信息显示区域 -->
|
||||
<div id="errorMessage" style="display: none;"></div>
|
||||
{% endblock body %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
function submitForm() {
|
||||
$.ajax({
|
||||
url: "{% url 'exam:register' %}", // Django模板标签,用于生成URL
|
||||
type: "POST",
|
||||
data: $('#registerForm').serialize(),
|
||||
success: function(response) {
|
||||
if (response.code === 200) {
|
||||
alert(response.message); // 成功时的消息框
|
||||
} else {
|
||||
displayErrors(response.data); // 显示错误信息
|
||||
alert(response.message);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert("An error occurred while processing your request.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayErrors(errors) {
|
||||
$('#errorMessage').html('');
|
||||
for (let key in errors) {
|
||||
if (errors.hasOwnProperty(key)) {
|
||||
let message = errors[key];
|
||||
$('#errorMessage').append('<p>' + message + '</p>');
|
||||
}
|
||||
}
|
||||
$('#errorMessage').show();
|
||||
}
|
||||
</script>
|
||||
{% endblock script %}
|
||||
0
不夜城考试/__init__.py
Normal file
0
不夜城考试/__init__.py
Normal file
16
不夜城考试/asgi.py
Normal file
16
不夜城考试/asgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for 不夜城考试 project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '不夜城考试.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
137
不夜城考试/settings.py
Normal file
137
不夜城考试/settings.py
Normal file
@ -0,0 +1,137 @@
|
||||
"""
|
||||
Django settings for 不夜城考试 project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.0.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-!78yd6se#zw2x9##j$x@$j9+q)cf!7%pham38azqz+p3j)chwe'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'exam',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = '不夜城考试.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates']
|
||||
,
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django.template.context_processors.media',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = '不夜城考试.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'static/'),
|
||||
os.path.join(BASE_DIR, 'media/'),
|
||||
]
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
MEDIA_URL = 'media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
|
||||
AUTH_USER_MODEL = 'exam.CyberUser'
|
||||
25
不夜城考试/urls.py
Normal file
25
不夜城考试/urls.py
Normal file
@ -0,0 +1,25 @@
|
||||
"""
|
||||
URL configuration for 不夜城考试 project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/5.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the "include()" function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf.urls.static import static
|
||||
from . import settings
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('exam/', include(('exam.urls', 'exam'), namespace='exam')),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
16
不夜城考试/wsgi.py
Normal file
16
不夜城考试/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for 不夜城考试 project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '不夜城考试.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
Loading…
Reference in New Issue
Block a user