school44s commited on
Commit
c5b63b4
·
verified ·
1 Parent(s): c6181b0

Upload 36 files

Browse files
.dockerignore ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .Python
6
+ env
7
+ venv
8
+ .env
9
+ .git
10
+ .gitignore
11
+ README.md
12
+ *.md
13
+ .vscode
14
+ .idea
15
+ *.log
.gitignore ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .Python
6
+ env
7
+ venv
8
+ .env
9
+ *.log
10
+ db.sqlite3
11
+ media/
12
+ staticfiles/
13
+ .vscode
14
+ .idea
15
+ *.md
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1
4
+ ENV PYTHONUNBUFFERED=1
5
+
6
+ WORKDIR /app
7
+
8
+ RUN apt-get update && apt-get install -y \
9
+ default-libmysqlclient-dev \
10
+ build-essential \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ COPY requirements.txt /app/
14
+ RUN pip install --no-cache-dir -r requirements.txt
15
+
16
+ COPY . /app/
17
+
18
+ EXPOSE 7860
19
+
20
+ CMD ["gunicorn", "--bind", "0.0.0.0:7860", "config.wsgi:application"]
config/__init__.py ADDED
File without changes
config/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (133 Bytes). View file
 
config/__pycache__/settings.cpython-313.pyc ADDED
Binary file (3.08 kB). View file
 
config/__pycache__/urls.cpython-313.pyc ADDED
Binary file (793 Bytes). View file
 
config/__pycache__/wsgi.cpython-313.pyc ADDED
Binary file (383 Bytes). View file
 
config/settings.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ BASE_DIR = Path(__file__).resolve().parent.parent
8
+
9
+ SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-dev-key-change-in-production')
10
+
11
+ DEBUG = os.getenv('DEBUG', 'True') == 'True'
12
+
13
+ ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '*').split(',')
14
+
15
+ INSTALLED_APPS = [
16
+ 'django.contrib.admin',
17
+ 'django.contrib.auth',
18
+ 'django.contrib.contenttypes',
19
+ 'django.contrib.sessions',
20
+ 'django.contrib.messages',
21
+ 'django.contrib.staticfiles',
22
+ 'rest_framework',
23
+ 'students',
24
+ ]
25
+
26
+ MIDDLEWARE = [
27
+ 'django.middleware.security.SecurityMiddleware',
28
+ 'django.contrib.sessions.middleware.SessionMiddleware',
29
+ 'django.middleware.common.CommonMiddleware',
30
+ 'django.middleware.csrf.CsrfViewMiddleware',
31
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
32
+ 'django.contrib.messages.middleware.MessageMiddleware',
33
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
34
+ ]
35
+
36
+ ROOT_URLCONF = 'config.urls'
37
+
38
+ TEMPLATES = [
39
+ {
40
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
41
+ 'DIRS': [],
42
+ 'APP_DIRS': True,
43
+ 'OPTIONS': {
44
+ 'context_processors': [
45
+ 'django.template.context_processors.debug',
46
+ 'django.template.context_processors.request',
47
+ 'django.contrib.auth.context_processors.auth',
48
+ 'django.contrib.messages.context_processors.messages',
49
+ ],
50
+ },
51
+ },
52
+ ]
53
+
54
+ WSGI_APPLICATION = 'config.wsgi.application'
55
+
56
+ DATABASES = {
57
+ 'default': {
58
+ 'ENGINE': 'django.db.backends.mysql',
59
+ 'NAME': os.getenv('DB_NAME', 'appdb'),
60
+ 'USER': os.getenv('DB_USER', 'root'),
61
+ 'PASSWORD': os.getenv('DB_PASSWORD', 'root'),
62
+ 'HOST': os.getenv('DB_HOST', '118.69.117.220'),
63
+ 'PORT': os.getenv('DB_PORT', '1611'),
64
+ 'OPTIONS': {
65
+ 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
66
+ 'charset': 'utf8mb4',
67
+ },
68
+ }
69
+ }
70
+
71
+ AUTH_PASSWORD_VALIDATORS = [
72
+ {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
73
+ {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
74
+ {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
75
+ {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
76
+ ]
77
+
78
+ LANGUAGE_CODE = 'en-us'
79
+ TIME_ZONE = 'UTC'
80
+ USE_I18N = True
81
+ USE_TZ = True
82
+
83
+ STATIC_URL = 'static/'
84
+ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
85
+
86
+ REST_FRAMEWORK = {
87
+ 'DEFAULT_RENDERER_CLASSES': [
88
+ 'rest_framework.renderers.JSONRenderer',
89
+ ],
90
+ 'DEFAULT_PARSER_CLASSES': [
91
+ 'rest_framework.parsers.JSONParser',
92
+ ],
93
+ }
config/urls.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.contrib import admin
2
+ from django.urls import path, include
3
+ from django.shortcuts import redirect
4
+ from students.urls import ui_urlpatterns
5
+
6
+ urlpatterns = [
7
+ path('admin/', admin.site.urls),
8
+ path('api/students/', include('students.urls')),
9
+ path('students/', include((ui_urlpatterns, 'students'), namespace='students')),
10
+ path('', lambda request: redirect('students:student-list-ui')),
11
+ ]
config/wsgi.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import os
2
+ from django.core.wsgi import get_wsgi_application
3
+
4
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
5
+
6
+ application = get_wsgi_application()
docker-compose.yml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ web:
5
+ build: .
6
+ ports:
7
+ - "7860:7860"
8
+ env_file:
9
+ - .env
10
+ volumes:
11
+ - .:/app
12
+ command: >
13
+ sh -c "python manage.py migrate &&
14
+ python manage.py runserver 0.0.0.0:7860"
15
+ restart: unless-stopped
manage.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ import os
3
+ import sys
4
+
5
+
6
+ def main():
7
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
8
+ try:
9
+ from django.core.management import execute_from_command_line
10
+ except ImportError as exc:
11
+ raise ImportError(
12
+ "Couldn't import Django. Are you sure it's installed and "
13
+ "available on your PYTHONPATH environment variable? Did you "
14
+ "forget to activate a virtual environment?"
15
+ ) from exc
16
+ execute_from_command_line(sys.argv)
17
+
18
+
19
+ if __name__ == '__main__':
20
+ main()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Django==5.0.6
2
+ djangorestframework==3.15.1
3
+ mysqlclient==2.2.4
4
+ python-dotenv==1.0.1
5
+ gunicorn==22.0.0
students/__init__.py ADDED
File without changes
students/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (135 Bytes). View file
 
students/__pycache__/admin.cpython-313.pyc ADDED
Binary file (775 Bytes). View file
 
students/__pycache__/apps.cpython-313.pyc ADDED
Binary file (503 Bytes). View file
 
students/__pycache__/models.cpython-313.pyc ADDED
Binary file (1.29 kB). View file
 
students/__pycache__/serializers.cpython-313.pyc ADDED
Binary file (858 Bytes). View file
 
students/__pycache__/urls.cpython-313.pyc ADDED
Binary file (1.04 kB). View file
 
students/__pycache__/views.cpython-313.pyc ADDED
Binary file (6.39 kB). View file
 
students/admin.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.contrib import admin
2
+ from .models import Student
3
+
4
+
5
+ @admin.register(Student)
6
+ class StudentAdmin(admin.ModelAdmin):
7
+ list_display = ['id', 'name', 'email', 'age', 'course', 'created_at']
8
+ list_filter = ['course', 'created_at']
9
+ search_fields = ['name', 'email']
10
+ readonly_fields = ['created_at', 'updated_at']
students/apps.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class StudentsConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ name = 'students'
students/migrations/0001_initial.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 6.0.6 on 2026-06-21 02:21
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ initial = True
9
+
10
+ dependencies = [
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='Student',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('name', models.CharField(max_length=100)),
19
+ ('email', models.EmailField(max_length=254, unique=True)),
20
+ ('age', models.IntegerField()),
21
+ ('course', models.CharField(max_length=100)),
22
+ ('created_at', models.DateTimeField(auto_now_add=True)),
23
+ ('updated_at', models.DateTimeField(auto_now=True)),
24
+ ],
25
+ options={
26
+ 'db_table': 'students',
27
+ 'ordering': ['-created_at'],
28
+ },
29
+ ),
30
+ ]
students/migrations/__init__.py ADDED
File without changes
students/migrations/__pycache__/0001_initial.cpython-313.pyc ADDED
Binary file (1.36 kB). View file
 
students/migrations/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (146 Bytes). View file
 
students/models.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.db import models
2
+
3
+
4
+ class Student(models.Model):
5
+ name = models.CharField(max_length=100)
6
+ email = models.EmailField(unique=True)
7
+ age = models.IntegerField()
8
+ course = models.CharField(max_length=100)
9
+ created_at = models.DateTimeField(auto_now_add=True)
10
+ updated_at = models.DateTimeField(auto_now=True)
11
+
12
+ class Meta:
13
+ db_table = 'students'
14
+ ordering = ['-created_at']
15
+
16
+ def __str__(self):
17
+ return self.name
students/serializers.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from rest_framework import serializers
2
+ from .models import Student
3
+
4
+
5
+ class StudentSerializer(serializers.ModelSerializer):
6
+ class Meta:
7
+ model = Student
8
+ fields = ['id', 'name', 'email', 'age', 'course', 'created_at', 'updated_at']
9
+ read_only_fields = ['id', 'created_at', 'updated_at']
students/templates/students/base.html ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}Student Management{% endblock %}</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ </head>
9
+ <body>
10
+ <nav class="navbar navbar-expand-lg navbar-dark bg-primary mb-4">
11
+ <div class="container">
12
+ <a class="navbar-brand" href="{% url 'students:student-list-ui' %}">Student Management</a>
13
+ </div>
14
+ </nav>
15
+ <div class="container">
16
+ {% if messages %}
17
+ {% for message in messages %}
18
+ <div class="alert alert-{{ message.tags }} alert-dismissible fade show">
19
+ {{ message }}
20
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
21
+ </div>
22
+ {% endfor %}
23
+ {% endif %}
24
+ {% block content %}{% endblock %}
25
+ </div>
26
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
27
+ </body>
28
+ </html>
students/templates/students/student_confirm_delete.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'students/base.html' %}
2
+
3
+ {% block title %}Delete Student{% endblock %}
4
+
5
+ {% block content %}
6
+ <h1>Delete Student</h1>
7
+ <p class="mt-3">Are you sure you want to delete <strong>{{ student.name }}</strong>?</p>
8
+ <form method="post">
9
+ {% csrf_token %}
10
+ <button type="submit" class="btn btn-danger">Yes, delete</button>
11
+ <a href="{% url 'students:student-list-ui' %}" class="btn btn-secondary">Cancel</a>
12
+ </form>
13
+ {% endblock %}
students/templates/students/student_form.html ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'students/base.html' %}
2
+
3
+ {% block title %}{{ title }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <h1>{{ title }}</h1>
7
+
8
+ <form method="post" class="mt-3">
9
+ {% csrf_token %}
10
+ <div class="mb-3">
11
+ <label class="form-label">Name</label>
12
+ <input type="text" name="name" class="form-control" value="{{ student.name|default:'' }}" required>
13
+ </div>
14
+ <div class="mb-3">
15
+ <label class="form-label">Email</label>
16
+ <input type="email" name="email" class="form-control" value="{{ student.email|default:'' }}" required>
17
+ </div>
18
+ <div class="mb-3">
19
+ <label class="form-label">Age</label>
20
+ <input type="number" name="age" class="form-control" value="{{ student.age|default:'' }}" required>
21
+ </div>
22
+ <div class="mb-3">
23
+ <label class="form-label">Course</label>
24
+ <input type="text" name="course" class="form-control" value="{{ student.course|default:'' }}" required>
25
+ </div>
26
+ <button type="submit" class="btn btn-success">Save</button>
27
+ <a href="{% url 'students:student-list-ui' %}" class="btn btn-secondary">Cancel</a>
28
+ </form>
29
+ {% endblock %}
students/templates/students/student_list.html ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'students/base.html' %}
2
+
3
+ {% block title %}Student List{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="d-flex justify-content-between align-items-center mb-3">
7
+ <h1>Students</h1>
8
+ <a href="{% url 'students:student-create-ui' %}" class="btn btn-primary">+ Add Student</a>
9
+ </div>
10
+
11
+ <table class="table table-striped table-hover">
12
+ <thead class="table-dark">
13
+ <tr>
14
+ <th>ID</th>
15
+ <th>Name</th>
16
+ <th>Email</th>
17
+ <th>Age</th>
18
+ <th>Course</th>
19
+ <th>Actions</th>
20
+ </tr>
21
+ </thead>
22
+ <tbody>
23
+ {% for student in students %}
24
+ <tr>
25
+ <td>{{ student.id }}</td>
26
+ <td>{{ student.name }}</td>
27
+ <td>{{ student.email }}</td>
28
+ <td>{{ student.age }}</td>
29
+ <td>{{ student.course }}</td>
30
+ <td>
31
+ <a href="{% url 'students:student-update-ui' student.id %}" class="btn btn-sm btn-warning">Edit</a>
32
+ <a href="{% url 'students:student-delete-ui' student.id %}" class="btn btn-sm btn-danger">Delete</a>
33
+ </td>
34
+ </tr>
35
+ {% empty %}
36
+ <tr>
37
+ <td colspan="6" class="text-center">No students found.</td>
38
+ </tr>
39
+ {% endfor %}
40
+ </tbody>
41
+ </table>
42
+ {% endblock %}
students/urls.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.urls import path
2
+ from . import views
3
+
4
+ urlpatterns = [
5
+ path('', views.student_list, name='student-list'),
6
+ path('create/', views.student_create, name='student-create'),
7
+ path('<int:pk>/', views.student_detail, name='student-detail'),
8
+ path('<int:pk>/delete/', views.student_delete, name='student-delete'),
9
+ ]
10
+
11
+ ui_urlpatterns = [
12
+ path('', views.student_list_ui, name='student-list-ui'),
13
+ path('add/', views.student_create_ui, name='student-create-ui'),
14
+ path('<int:pk>/edit/', views.student_update_ui, name='student-update-ui'),
15
+ path('<int:pk>/delete/', views.student_delete_ui, name='student-delete-ui'),
16
+ ]
students/views.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.shortcuts import render, redirect, get_object_or_404
2
+ from django.contrib import messages
3
+ from rest_framework import status
4
+ from rest_framework.decorators import api_view
5
+ from rest_framework.response import Response
6
+ from .models import Student
7
+ from .serializers import StudentSerializer
8
+
9
+
10
+ @api_view(['GET'])
11
+ def student_list(request):
12
+ students = Student.objects.all()
13
+ serializer = StudentSerializer(students, many=True)
14
+ return Response(serializer.data)
15
+
16
+
17
+ @api_view(['POST'])
18
+ def student_create(request):
19
+ serializer = StudentSerializer(data=request.data)
20
+ if serializer.is_valid():
21
+ serializer.save()
22
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
23
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
24
+
25
+
26
+ @api_view(['GET', 'PUT', 'PATCH'])
27
+ def student_detail(request, pk):
28
+ try:
29
+ student = Student.objects.get(pk=pk)
30
+ except Student.DoesNotExist:
31
+ return Response({'error': 'Student not found'}, status=status.HTTP_404_NOT_FOUND)
32
+
33
+ if request.method == 'GET':
34
+ serializer = StudentSerializer(student)
35
+ return Response(serializer.data)
36
+
37
+ elif request.method in ['PUT', 'PATCH']:
38
+ serializer = StudentSerializer(student, data=request.data, partial=(request.method == 'PATCH'))
39
+ if serializer.is_valid():
40
+ serializer.save()
41
+ return Response(serializer.data)
42
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
43
+
44
+
45
+ @api_view(['DELETE'])
46
+ def student_delete(request, pk):
47
+ try:
48
+ student = Student.objects.get(pk=pk)
49
+ except Student.DoesNotExist:
50
+ return Response({'error': 'Student not found'}, status=status.HTTP_404_NOT_FOUND)
51
+
52
+ student.delete()
53
+ return Response(status=status.HTTP_204_NO_CONTENT)
54
+
55
+
56
+ def student_list_ui(request):
57
+ students = Student.objects.all()
58
+ return render(request, 'students/student_list.html', {'students': students})
59
+
60
+
61
+ def student_create_ui(request):
62
+ if request.method == 'POST':
63
+ name = request.POST.get('name')
64
+ email = request.POST.get('email')
65
+ age = request.POST.get('age')
66
+ course = request.POST.get('course')
67
+ try:
68
+ Student.objects.create(name=name, email=email, age=int(age), course=course)
69
+ messages.success(request, 'Student added successfully.')
70
+ return redirect('students:student-list-ui')
71
+ except Exception as e:
72
+ messages.error(request, f'Error: {e}')
73
+ return render(request, 'students/student_form.html', {'title': 'Add Student'})
74
+
75
+
76
+ def student_update_ui(request, pk):
77
+ student = get_object_or_404(Student, pk=pk)
78
+ if request.method == 'POST':
79
+ student.name = request.POST.get('name')
80
+ student.email = request.POST.get('email')
81
+ student.age = int(request.POST.get('age'))
82
+ student.course = request.POST.get('course')
83
+ try:
84
+ student.save()
85
+ messages.success(request, 'Student updated successfully.')
86
+ return redirect('students:student-list-ui')
87
+ except Exception as e:
88
+ messages.error(request, f'Error: {e}')
89
+ return render(request, 'students/student_form.html', {'title': 'Edit Student', 'student': student})
90
+
91
+
92
+ def student_delete_ui(request, pk):
93
+ student = get_object_or_404(Student, pk=pk)
94
+ if request.method == 'POST':
95
+ student.delete()
96
+ messages.success(request, 'Student deleted successfully.')
97
+ return redirect('students:student-list-ui')
98
+ return render(request, 'students/student_confirm_delete.html', {'student': student})