diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 4afdb9de..5fabe732 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -82,6 +82,7 @@ THIRD_PARTY_APPS = [ "rest_framework.authtoken", "corsheaders", "drf_spectacular", + "django_htmx", ] LOCAL_APPS = [ @@ -145,6 +146,7 @@ MIDDLEWARE = [ "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.common.BrokenLinkEmailsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django_htmx.middleware.HtmxMiddleware", "vbv_lernwelt.core.middleware.auth.AuthenticationRequiredMiddleware", "vbv_lernwelt.core.middleware.security.SecurityRequestResponseLoggingMiddleware", ] diff --git a/server/config/urls.py b/server/config/urls.py index a353b863..23311735 100644 --- a/server/config/urls.py +++ b/server/config/urls.py @@ -11,26 +11,15 @@ from rest_framework.authtoken.views import obtain_auth_token from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt +# fmt: off urlpatterns = [ - path( - "", - django_view_authentication_exempt( - TemplateView.as_view(template_name="pages/home.html") - ), - name="home", - ), - path( - "about/", TemplateView.as_view(template_name="pages/about.html"), name="about" - ), + path("", django_view_authentication_exempt(TemplateView.as_view(template_name="pages/home.html")), name="home"), + path("about/", TemplateView.as_view(template_name="pages/about.html"), name="about"), # Django Admin, use {% url 'admin:index' %} path(settings.ADMIN_URL, admin.site.urls), # Your stuff: custom urls includes go here - path( - "login/", - django_view_authentication_exempt( - auth_views.LoginView.as_view(template_name="core/login.html") - ), - ), + path("login/", django_view_authentication_exempt(auth_views.LoginView.as_view(template_name="core/login.html"))), + path("todo/", include("vbv_lernwelt.simpletodo.urls")), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: # Static file serving when using Gunicorn + Uvicorn for local web socket development @@ -43,12 +32,9 @@ urlpatterns += [ # DRF auth token path("auth-token/", obtain_auth_token), path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"), - path( - "api/docs/", - SpectacularSwaggerView.as_view(url_name="api-schema"), - name="api-docs", - ), + path("api/docs/", SpectacularSwaggerView.as_view(url_name="api-schema"), name="api-docs",), ] +# fmt: on if settings.DEBUG: # This allows the error pages to be debugged during development, just visit diff --git a/server/example.env b/server/example.env index f5e90aee..1c3ab747 100644 --- a/server/example.env +++ b/server/example.env @@ -1,3 +1,3 @@ export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt' -#export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR +export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR export VBV_DJANGO_DEBUG=True diff --git a/server/requirements/requirements-dev.txt b/server/requirements/requirements-dev.txt index 59276d67..700275ed 100644 --- a/server/requirements/requirements-dev.txt +++ b/server/requirements/requirements-dev.txt @@ -69,6 +69,7 @@ django==3.2.12 # django-cors-headers # django-debug-toolbar # django-extensions + # django-htmx # django-model-utils # django-redis # django-stubs @@ -83,6 +84,8 @@ django-debug-toolbar==3.2.4 # via -r requirements-dev.in django-extensions==3.1.5 # via -r requirements-dev.in +django-htmx==1.8.0 + # via -r requirements.in django-model-utils==4.2.0 # via -r requirements.in django-redis==5.2.0 diff --git a/server/requirements/requirements.in b/server/requirements/requirements.in index 631571f5..3cebca46 100644 --- a/server/requirements/requirements.in +++ b/server/requirements/requirements.in @@ -19,6 +19,7 @@ djangorestframework # https://github.com/encode/django-rest-framework django-cors-headers # https://github.com/adamchainz/django-cors-headers # DRF-spectacular for api documentation drf-spectacular +django-htmx psycopg2-binary gunicorn diff --git a/server/requirements/requirements.txt b/server/requirements/requirements.txt index 81bd8fd2..ee060681 100644 --- a/server/requirements/requirements.txt +++ b/server/requirements/requirements.txt @@ -30,12 +30,15 @@ django==3.2.12 # via # -r requirements.in # django-cors-headers + # django-htmx # django-model-utils # django-redis # djangorestframework # drf-spectacular django-cors-headers==3.11.0 # via -r requirements.in +django-htmx==1.8.0 + # via -r requirements.in django-model-utils==4.2.0 # via -r requirements.in django-redis==5.2.0 diff --git a/server/vbv_lernwelt/core/tests/factories.py b/server/vbv_lernwelt/core/tests/factories.py index dfe56660..02da3fbc 100644 --- a/server/vbv_lernwelt/core/tests/factories.py +++ b/server/vbv_lernwelt/core/tests/factories.py @@ -3,7 +3,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.hashers import make_password -class SimpleUserFactory(factory.django.DjangoModelFactory): +class UserFactory(factory.django.DjangoModelFactory): class Meta: model = get_user_model() django_get_or_create = ("username",) diff --git a/server/vbv_lernwelt/simpletodo/admin.py b/server/vbv_lernwelt/simpletodo/admin.py index 8c38f3f3..e18c8a68 100644 --- a/server/vbv_lernwelt/simpletodo/admin.py +++ b/server/vbv_lernwelt/simpletodo/admin.py @@ -1,3 +1,39 @@ +# Register your models here. from django.contrib import admin -# Register your models here. +from .models import SimpleTask, SimpleList + + +@admin.register(SimpleList) +class SimpleListAdmin(admin.ModelAdmin): + list_display = [ + "title", + "user", + "created", + ] + list_filter = [ + "user", + ] + search_fields = [ + "title", + ] + + +@admin.register(SimpleTask) +class SimpleTaskAdmin(admin.ModelAdmin): + date_hierarchy = "deadline" + list_display = [ + "title", + "deadline", + "created", + "list", + "done", + ] + list_filter = [ + "list", + "done", + ] + search_fields = [ + "title", + "text", + ] diff --git a/server/vbv_lernwelt/simpletodo/apps.py b/server/vbv_lernwelt/simpletodo/apps.py index 4524d7c3..45efffa1 100644 --- a/server/vbv_lernwelt/simpletodo/apps.py +++ b/server/vbv_lernwelt/simpletodo/apps.py @@ -2,5 +2,5 @@ from django.apps import AppConfig class SimpletodoConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'vbv_lernwelt.simpletodo' + default_auto_field = "django.db.models.BigAutoField" + name = "vbv_lernwelt.simpletodo" diff --git a/server/vbv_lernwelt/simpletodo/migrations/0001_initial.py b/server/vbv_lernwelt/simpletodo/migrations/0001_initial.py index 0ebfc0cb..e850a29e 100644 --- a/server/vbv_lernwelt/simpletodo/migrations/0001_initial.py +++ b/server/vbv_lernwelt/simpletodo/migrations/0001_initial.py @@ -18,32 +18,88 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='SimpleList', + name="SimpleList", fields=[ - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('title', models.CharField(max_length=255)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("title", models.CharField(max_length=255)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='SimpleTask', + name="SimpleTask", fields=[ - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('title', models.CharField(max_length=255)), - ('text', models.TextField(blank=True, default='')), - ('done', models.BooleanField(default=False)), - ('deadline', models.DateTimeField(blank=True, null=True)), - ('list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='simpletodo.simplelist')), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("title", models.CharField(max_length=255)), + ("text", models.TextField(blank=True, default="")), + ("done", models.BooleanField(default=False)), + ("deadline", models.DateTimeField(blank=True, null=True)), + ( + "list", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="simpletodo.simplelist", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/server/vbv_lernwelt/simpletodo/models.py b/server/vbv_lernwelt/simpletodo/models.py index bc06a0bb..042b46b1 100644 --- a/server/vbv_lernwelt/simpletodo/models.py +++ b/server/vbv_lernwelt/simpletodo/models.py @@ -10,6 +10,9 @@ class SimpleList(TimeStampedModel): title = models.CharField(max_length=255) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + def __str__(self): + return f"{self.title} ({self.user})" + class SimpleTask(TimeStampedModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) diff --git a/server/vbv_lernwelt/simpletodo/serializers.py b/server/vbv_lernwelt/simpletodo/serializers.py new file mode 100644 index 00000000..bb58d995 --- /dev/null +++ b/server/vbv_lernwelt/simpletodo/serializers.py @@ -0,0 +1,23 @@ +from rest_framework import serializers +from rest_framework.serializers import ModelSerializer + +from vbv_lernwelt.simpletodo.models import SimpleTask, SimpleList + + +class SimpleTaskSerializer(ModelSerializer): + list_title = serializers.CharField(max_length=100) + + class Meta: + model = SimpleTask + fields = ['id', 'title', 'text', 'done', 'deadline', 'list_title', ] + + def create(self, validated_data): + user = validated_data.pop('user', None) + if user is None: + raise serializers.ValidationError('User is required') + + list_title = validated_data.pop('list_title') + simple_list, _ = SimpleList.objects.get_or_create(title=list_title, user=user) + + validated_data['list'] = simple_list + return super().create(validated_data) diff --git a/server/vbv_lernwelt/simpletodo/templates/simpletodo/index.html b/server/vbv_lernwelt/simpletodo/templates/simpletodo/index.html new file mode 100644 index 00000000..13b6df51 --- /dev/null +++ b/server/vbv_lernwelt/simpletodo/templates/simpletodo/index.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + + +{% block content %} + +
+ {% else %} +
+ {% endif %} + {{ task.title }} +
+ + + +
- Hier steht noch etwas mehr Text
-weitere Kurse entdecken und buchen
- - -
+ Hier steht noch etwas mehr Text
+weitere Kurse entdecken und buchen
+ + +