Skip to content

Commit 6c523f9

Browse files
Reordering Questions: Admin functionality (#17)
* updated requirements.txt for sortable objects * feat: admin can now sort questions * updated README for prod config + removed backports dependency
1 parent 44a889e commit 6c523f9

11 files changed

+168
-13
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ secret.key
44
.venv
55
.env
66
venv
7-
.env
7+
.env

README.md

+76-2
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ Replace:
4646
* Create a new file ```.env``` and add in the secrets needed
4747

4848
```bash
49-
CLIENT_ID = XXX.apps.googleusercontent.com
49+
CLIENT_ID=XXX.apps.googleusercontent.com
5050
CLIENT_SECRET=XXX-YYY-ZZZ
51-
SECRET_KEY = XXX
51+
SECRET_KEY=XXX
5252
```
5353

5454
## Usage
@@ -69,3 +69,77 @@ python manage.py runserver 0.0.0.0:8000
6969
* A leaderboard showcasing the `User`s with the most points.
7070

7171
Ties are settled based on the time taken to complete `Task`s
72+
73+
## Configuration for Production
74+
75+
### Adding environment variables for MYSQL
76+
77+
* Update the new file ```.env``` and add in the secrets needed for using MySQL
78+
79+
```bash
80+
CLIENT_ID=XXX.apps.googleusercontent.com
81+
CLIENT_SECRET=XXX-YYY-ZZZ
82+
SECRET_KEY=XXX
83+
MYSQL_HOST=XXX
84+
MYSQL_USERNAME=XXX
85+
MYSQL_DATABASE=‘XXX’
86+
MYSQL_PASSWORD=XXX
87+
```
88+
89+
### Change backend to MySQL
90+
91+
In [settings.py](mouseless/settings.py), update the backend from SQLite3 to MySQL.
92+
93+
Change this:
94+
95+
```python
96+
DATABASES = {
97+
'default': {
98+
'ENGINE': 'django.db.backends.sqlite3',
99+
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
100+
}
101+
}
102+
```
103+
To this:
104+
```python
105+
DATABASES = {
106+
'default': {
107+
'ENGINE': 'django.db.backends.mysql',
108+
'NAME': os.getenv('MYSQL_DATABASE'),
109+
'USER': os.getenv('MYSQL_USERNAME'),
110+
'PASSWORD': os.getenv('MYSQL_PASSWORD'),
111+
'HOST': os.getenv('MYSQL_HOST')
112+
}
113+
}
114+
```
115+
116+
### Updating the static path
117+
118+
In [settings.py](mouseless/settings.py), comment out the `STATIC_DIR` variable and add `STATIC_ROOT` as below:
119+
```bash
120+
# STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
121+
STATIC_ROOT = os.path.join(BASE_DIR, "static")
122+
```
123+
Then in the bash console, run the command:
124+
```
125+
python manage.py collectstatic
126+
```
127+
This will now put all static files into one static folder
128+
129+
Now, add this static folder path into PythonAnywhere's Web section:
130+
131+
| URL | Directory |
132+
| -------- | ------- |
133+
| /static/ | /home/{{USERNAME}}/{{PATH_TO_STATIC_FOLDER}} |
134+
135+
If you're adding the reorder functionality to an already existing db, run the command:
136+
```bash
137+
python manage.py reorder quiz.Task
138+
```
139+
This will now set the order attr of each record
140+
141+
Then you can migrate the changes:
142+
```bash
143+
python manage.py makemigrations
144+
python manage.py migrate
145+
```

db.sqlite3

12 KB
Binary file not shown.

mouseless/settings.py

+16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import os
2+
from dotenv import load_dotenv, find_dotenv
3+
4+
load_dotenv(find_dotenv())
25

36
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
47
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -40,6 +43,7 @@
4043
'crispy_forms',
4144
'phonenumber_field',
4245
'markdownx',
46+
'adminsortable2',
4347
'django.contrib.admin',
4448
'django.contrib.auth',
4549
'django.contrib.contenttypes',
@@ -126,6 +130,18 @@
126130
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
127131
}
128132
}
133+
# Use below for prod
134+
"""
135+
DATABASES = {
136+
'default': {
137+
'ENGINE': 'django.db.backends.mysql',
138+
'NAME': os.getenv('MYSQL_DATABASE'),
139+
'USER': os.getenv('MYSQL_USERNAME'),
140+
'PASSWORD': os.getenv('MYSQL_PASSWORD'),
141+
'HOST': os.getenv('MYSQL_HOST')
142+
}
143+
}
144+
"""
129145

130146

131147
# Password validation

quiz/admin.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from django.contrib import admin
22
from .models import Task, Card, Answer, Hint
33
from markdownx.admin import MarkdownxModelAdmin
4+
from adminsortable2.admin import SortableAdminMixin
45

56
@admin.register(Task)
6-
class TaskAdmin(MarkdownxModelAdmin):
7+
class TaskAdmin(SortableAdminMixin, MarkdownxModelAdmin):
78
fields = ('name', 'text', 'points', 'correct','hint','hint_points')
89
list_display = ('name', 'points', 'correct',)
910

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 4.2 on 2024-09-01 15:38
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('quiz', '0006_hint'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='task',
15+
options={'ordering': ['points']},
16+
),
17+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 4.2 on 2024-09-01 15:52
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('quiz', '0007_alter_task_options'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='task',
15+
options={'ordering': ['my_order']},
16+
),
17+
migrations.AddField(
18+
model_name='task',
19+
name='my_order',
20+
field=models.PositiveIntegerField(default=0),
21+
),
22+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 4.2 on 2024-09-01 18:25
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('quiz', '0008_alter_task_options_task_my_order'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='task',
15+
options={'ordering': ['order']},
16+
),
17+
migrations.RenameField(
18+
model_name='task',
19+
old_name='my_order',
20+
new_name='order',
21+
),
22+
]

quiz/models.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ class Task(models.Model):
1414
correct = models.CharField(max_length=256)
1515
hint = models.CharField(max_length=256, default='No hint!!')
1616
hint_points = models.IntegerField(default=0)
17+
order = models.PositiveIntegerField(
18+
default=0,
19+
blank=False,
20+
null=False,
21+
)
22+
23+
class Meta:
24+
ordering = ['order']
1725

1826
@property
1927
def formatted_markdown(self):
@@ -22,7 +30,6 @@ def formatted_markdown(self):
2230
def get_absolute_url(self):
2331
return reverse('task-detail', kwargs={'pk': self.pk})
2432

25-
2633
class Card(models.Model):
2734
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
2835
start = models.DateTimeField(auto_now_add=True, auto_now=False)
@@ -43,15 +50,13 @@ def last_time(self):
4350

4451
return str(last_time - self.start)
4552

46-
4753
class Answer(models.Model):
4854
card = models.ForeignKey(Card, on_delete=models.CASCADE)
4955
task = models.ForeignKey(Task, on_delete=models.CASCADE)
5056
value = models.CharField(max_length=256)
5157
submit = models.DateTimeField(auto_now=True)
5258
time_submitted = models.DateTimeField(auto_now=True)
5359

54-
5560
class Meta:
5661
unique_together = (('card', 'task'),)
5762

quiz/views.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ def register(request):
3333
form = UserRegisterForm()
3434
return render(request, 'quiz/register.html', {'form': form})
3535

36-
3736
class TaskListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
3837
model = Task
3938
template_name = 'quiz/task_list.html'
@@ -57,7 +56,7 @@ def get_context_data(self, **kwargs):
5756
return context
5857

5958
def get_queryset(self):
60-
return Task.objects.all().order_by('points')
59+
return Task.objects.all().order_by('order', 'points')
6160

6261

6362

requirements.txt

+3-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ charset-normalizer==3.3.2
77
cryptography==43.0.0
88
defusedxml==0.7.1
99
dj-database-url==1.0.0
10-
Django==4.1
10+
Django==4.2
11+
django-admin-sortable2==2.2.2
1112
django-allauth==0.63.6
1213
django-cors-headers==4.4.0
1314
django-crispy-forms==1.14.0
@@ -21,7 +22,6 @@ gunicorn==20.1.0
2122
idna==3.8
2223
importlib-metadata==4.12.0
2324
jwcrypto==1.5.6
24-
jwt==1.3.1
2525
Markdown==3.4.1
2626
oauthlib==3.2.2
2727
phonenumbers==8.12.54
@@ -36,10 +36,9 @@ pytz==2024.1
3636
PyYAML==6.0.2
3737
requests==2.32.3
3838
requests-oauthlib==2.0.0
39-
setuptools==73.0.1
4039
sqlparse==0.4.2
4140
typing_extensions==4.12.2
4241
tzdata==2022.2
4342
urllib3==2.2.2
4443
whitenoise==5.0.1
45-
zipp==3.8.1
44+
zipp==3.8.1

0 commit comments

Comments
 (0)