From e591d0f4092e2d37b2aa892fed302619b8907856 Mon Sep 17 00:00:00 2001 From: zwnk Date: Thu, 16 Jan 2025 10:20:20 -0300 Subject: [PATCH] second --- .env | 6 + Dockerfile | 30 ++++ db.sqlite3 | Bin 0 -> 139264 bytes entrypoint.sh | 17 ++ manage.py | 22 +++ requirements.txt | 7 + todo_project/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 189 bytes .../__pycache__/settings.cpython-312.pyc | Bin 0 -> 2855 bytes todo_project/__pycache__/urls.cpython-312.pyc | Bin 0 -> 1140 bytes todo_project/__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 687 bytes todo_project/asgi.py | 16 ++ todo_project/settings.py | 151 ++++++++++++++++++ todo_project/urls.py | 24 +++ todo_project/wsgi.py | 16 ++ todos/__init__.py | 0 todos/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 182 bytes todos/__pycache__/admin.cpython-312.pyc | Bin 0 -> 226 bytes todos/__pycache__/apps.cpython-312.pyc | Bin 0 -> 486 bytes todos/__pycache__/models.cpython-312.pyc | Bin 0 -> 1046 bytes todos/__pycache__/serializers.cpython-312.pyc | Bin 0 -> 1570 bytes todos/__pycache__/urls.cpython-312.pyc | Bin 0 -> 721 bytes todos/__pycache__/views.cpython-312.pyc | Bin 0 -> 3614 bytes todos/admin.py | 3 + todos/apps.py | 6 + todos/migrations/0001_initial.py | 27 ++++ todos/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-312.pyc | Bin 0 -> 1566 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 193 bytes todos/models.py | 12 ++ todos/serializers.py | 21 +++ todos/tests.py | 3 + todos/urls.py | 12 ++ todos/views.py | 51 ++++++ 34 files changed, 424 insertions(+) create mode 100644 .env create mode 100644 Dockerfile create mode 100644 db.sqlite3 create mode 100644 entrypoint.sh create mode 100755 manage.py create mode 100644 requirements.txt create mode 100644 todo_project/__init__.py create mode 100644 todo_project/__pycache__/__init__.cpython-312.pyc create mode 100644 todo_project/__pycache__/settings.cpython-312.pyc create mode 100644 todo_project/__pycache__/urls.cpython-312.pyc create mode 100644 todo_project/__pycache__/wsgi.cpython-312.pyc create mode 100644 todo_project/asgi.py create mode 100644 todo_project/settings.py create mode 100644 todo_project/urls.py create mode 100644 todo_project/wsgi.py create mode 100644 todos/__init__.py create mode 100644 todos/__pycache__/__init__.cpython-312.pyc create mode 100644 todos/__pycache__/admin.cpython-312.pyc create mode 100644 todos/__pycache__/apps.cpython-312.pyc create mode 100644 todos/__pycache__/models.cpython-312.pyc create mode 100644 todos/__pycache__/serializers.cpython-312.pyc create mode 100644 todos/__pycache__/urls.cpython-312.pyc create mode 100644 todos/__pycache__/views.cpython-312.pyc create mode 100644 todos/admin.py create mode 100644 todos/apps.py create mode 100644 todos/migrations/0001_initial.py create mode 100644 todos/migrations/__init__.py create mode 100644 todos/migrations/__pycache__/0001_initial.cpython-312.pyc create mode 100644 todos/migrations/__pycache__/__init__.cpython-312.pyc create mode 100644 todos/models.py create mode 100644 todos/serializers.py create mode 100644 todos/tests.py create mode 100644 todos/urls.py create mode 100644 todos/views.py diff --git a/.env b/.env new file mode 100644 index 0000000..f23dff0 --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +DB_NAME=todo_db +DB_USER=postgres +DB_PASSWORD=postgres +DB_HOST=db +DB_PORT=5432 +DEBUG=1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9e859df --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# backend/Dockerfile +FROM python:3.9-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# Set work directory +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + postgresql-client \ + netcat-openbsd \ + && rm -rf /var/lib/apt/lists/* + +# Install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy project +COPY . . + +# Copy entrypoint script and set permissions +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh \ + && sed -i 's/\r$//g' /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..4b381ffda5cbc98e0091abe2552f6521aaf480c9 GIT binary patch literal 139264 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 Sz_tciLRiB7uHVG=0{;&kJTvqF literal 0 HcmV?d00001 diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..60bffb8 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# Wait for postgres to be ready +while ! nc -z db 5432; do + echo "Waiting for postgres..." + sleep 1 +done + +echo "PostgreSQL started" + +# Apply database migrations +echo "Applying database migrations..." +python manage.py migrate + +# Start server +echo "Starting server..." +python manage.py runserver 0.0.0.0:8000 diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..1d0193a --- /dev/null +++ b/manage.py @@ -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', 'todo_project.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() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0598d25 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +Django==4.2.0 +djangorestframework==3.14.0 +django-cors-headers==4.1.0 +djangorestframework-simplejwt==5.2.2 +python-dotenv==1.0.0 +psycopg2-binary==2.9.9 + diff --git a/todo_project/__init__.py b/todo_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/todo_project/__pycache__/__init__.cpython-312.pyc b/todo_project/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..447f091e60a02e57f82931b60e792eec830f5709 GIT binary patch literal 189 zcmX@j%ge<81V6U6r-SInAOanHW&w&!XQ*V*Wb|9fP{ah}eFmxd6{DY#pPQG5r~UHjE~HWjEqIhKo$W1D>M-R literal 0 HcmV?d00001 diff --git a/todo_project/__pycache__/settings.cpython-312.pyc b/todo_project/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fcd5e7a8e65170d2a22b07c926edfee9b0eb19dc GIT binary patch literal 2855 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 literal 0 HcmV?d00001 diff --git a/todos/__pycache__/apps.cpython-312.pyc b/todos/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a632f60cc6272da52c81ba0c0b7197f1a84b897d GIT binary patch literal 486 zcmXv~Jxjzu5Z%p(ocAGeieO=3Bc#c;79yOU+8qdDk!IP;?!}x*HfA@%*$D@BVt2nm z`~&_3!N&3kSXtQ#r;U}nN!()Qy`A@lnR)GWnn1mm=eyuu8^$PfZ+HEcep%kSC)s@T+naVNDk63d{fl9;CoCDD+Hf zc-F*ptfFfZJ< z{ZrNlj#;$oZM8YmcdhJgjY{E)LPcY*+HXHwRaBo;=yQIV5W;t`{k>$swFl=1s8qWC E0BAgb0ssI2 literal 0 HcmV?d00001 diff --git a/todos/__pycache__/models.cpython-312.pyc b/todos/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02a116482c2fe238cdd2af753f2856c12bcf165a GIT binary patch literal 1046 zcmZuw%WD%s7@y7V=DD#6ifD|WlpcbT1(D(*2(?K>#a^2Bu!j!YoiPu0ck1k{n&Kgc zg6*lj)jy%70lj+jD2QMokg(8`r{09<#gj9;=|gl5-~PV${J#12OE#MZ{CxfRw&_R! zd>6{eQ72$r=U^8gfFv6@k|9X~$LyFR8?ppq;2}Wr20#jyI?0pdv+sc%VMFbzrTDQ> zN>UJa2*S3&Rrj+qMXG^(wJ^2fNJEtF>jFVUI!7 zG2JGCFMX`9n$~mdBAsy_aE+1q=1xW~9)4>o1X$W~K-xgSd@P%*Mk%tPZLAp*TZa3X0xJ$))1c~{% z#re5vEl_;EK0#`}Wzu>Z+lXd`TYEr=jZHT+1u2U-Yhvyw$W~2;8*K+ip0q_VHy~&- zD%N8k&^-UpDIww~LUi<&xYz)rOmTB|m?<7+itieG%kNkAR|c6%uR79-oAseqJk*MN z@<6-YtBrEgJMm#|`Y<=WR~zK+^d61!Gdq`u`I*D~%!j*!{Jq}eu_CF3sH14MP>Khs zXzpdS8Zj?Sbc?u*wpYu1En4Mh>d_?Br{YhFE_+?v_>@49tVA=tPj7OEczf>}7e8c4 al8!*}2;BOqB&5RT>~>|V@(XYnCjSBRP4)x; literal 0 HcmV?d00001 diff --git a/todos/__pycache__/serializers.cpython-312.pyc b/todos/__pycache__/serializers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..feeabdbbb236422a41191716d4eb6f2b5a006e8e GIT binary patch literal 1570 zcmah}&2Jk;6rY*>h+`*lN@5zy0YgyBs*<-t9QcSh6(J$jmjr|sq}66;l5Vp0hM9GP zDPh_P(>s6m)nn!g_37JG`@=!A^PLs3UY`FH&khlU!H#Ws-|9>!$sUwl||I zVTci7sH~ir;rxqLKJRyIz}AUpwa2=S-U3h zVPopm&F30~sCNOrCx6tJei$4Lp4D%i)Nh?ORv&+M((udq+#!qa42iW^J+#j&1Zb=k zl(jua)*)RkB3wg266G5R4TOSNJc+E7fIKOTJs>Zb<$U-T0T@;tXW2w!;Do}jdZr1U zvBLkWXZn_B>jmt(^(#G0)WXKZFOaG+D660Su0(UCh=W!tl-3m2(V<=(_C5OBj$ zxfhH%Ykv~}Qx4s2Z&oiz?5!;cy$pT6D^WH2hUv9doON@VY__6ae!o@pu`+seM+>}` zuHVBB9}{h!xH!b*!E|K}^5CE1V|g1ADf|l6om-UBGjj8c+&&{K7j}&<|M=Cj#+{SK UoeKhsiyIbQE5H0hU{T!RUpSXuR{#J2 literal 0 HcmV?d00001 diff --git a/todos/__pycache__/urls.cpython-312.pyc b/todos/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42eb5938c0da345d9f14abcec311db5b995ee45a GIT binary patch literal 721 zcmYLGy=xRf6rb6h{kqHMK*$la5Cw}>xwR2V5UUeA!4FQ@fX8NMa=FXS&M~u_kXV>h ziP)S~EG?BwVe3C&VLj3@}Uqg%PAiWI*j3sYy|U6gDwREow(Lbs~qlkqdzV2pV6sh&lw+V&q=Q@RJL0P z!euRGy?!>uR<7!VMB1ok?rJPZ7E>a@j#sfl=~hscAf4W^%O0V$O8YqQWgQRVtjAgf zPX*V{!jV$4$%)9jYh01G7~g2|a!tW4owPR9hJx!4Tq>J zCO(04l>q;qgzJow5XX6u^%KUku+5T!k}MZt`(Y={_2B7J-OYl8t8ubHG92ntyF<WD~5=l{{WLdIW$EH6>wT(aI)+rpwi0z`TBPn)rHgKSME1Aq4 zsqP&uNT3h}Fc2AiumTj2`&9R-HY^}{N?-aP^o4{}P(Ii&g1$5lE#%5Tfj)I+Pe)R= z3KThjv$H!p`!VyIndM))y21p$e|+=B?4N>!{0%$a2YvqS{9Pbqjp#%d3}OnZV2Y|} z`c$8QW1=CMe$~%;pAj&FYS5HbnfE0lWQNr+=lw>+jH*%K1A5TtGP~7o&dY{k_NYB( zui7gRkkL9;qk8!|Es!21h#?6G9;I|!Yop^{2 zmCnDty538ZdTc%J?ZUNEI-H88LF2u*H9Xrr6DyDsTRrVG0e;g<{mMK|n>a;VxZS&fu-&A{jFlJyqAmV}y6@^{Ky-g67P z2y!nFI;MH@CD9If!zCU{Ue9f7qUVk!pWWrjbkSBkzUC|ScznC{w2G~hPLZ}goGI3x z>v49%<4Q@7Yq#q@4-iu%BV9#v>v=ox_-%B-6M>->GEvqI4eJHs$VE%L3og(d$;#iQ z46o3ZUS
    F%*H7qlwik&BWnG z;_&)RGkdv_y}TK@QkSmmz}94cSdVd71DJ8j8((QR|8x%PE!Y6S|9`DESVg-P6g8nH zw!yRHtNGX|yA={8xpQBaulYRqE=hL8lj~9kfTa@R?2XUU+5`HwzF!X%1-lh2`-Q-I zumj}sRYyidmGdTbFIw^yI*T6!wqHlQFuc+zIH!_XR`L=|+Riqxk{)L)9W z=CznvK@IA{J)+9G2sE_dgB%dSG=Cjx#uT;pz#ajOza5188nIi;?3c^;%iaKa%ua87 z`$4r`38u&s5wapfKY{n~5>Bvk8*>K~y_uo`xx)1+p8@I9qQ;Dcrr3346!4)zR$~iL zH5XGIx65`)-T@5@(YH*VD{;;l4E(-q2AlTNgT>8#==r#d1h~1mp?fJMrjacrH#x zfw2(j_sCZN;kuOA8aUh>IMo<9RZpL;OUZvpg8!#G0l`1!YNH~&RiwGRea8tF%LSvV zd-=8)434=7ukVIYOX_9;VTa09rdw|}oV3tj$Na|?I+4XI9{u;y(n+?d8?AiN}J8|3T@a^fX< zALt9@Q!mMxZ-vmx>6N1!Lj0K!U%lDv%QX5j^~A9a;rJ_27Jeb@_=tD{Nn%^V{;LEL z!~a_)1pl%+Rj~g$DGOt(N8R_vYXY5rCyDUR3o`bSj5o;mx1r>3hL;0dB=Cjuxw0xh zJpOfHS=l6KUImChv=Y02WA*yl%`b0mbRF7|MxROY%HXQ`_}Hd&3i;IPxkn#tN+-61 Lk|KT{;r941)KVFN literal 0 HcmV?d00001 diff --git a/todos/admin.py b/todos/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/todos/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/todos/apps.py b/todos/apps.py new file mode 100644 index 0000000..a8b463e --- /dev/null +++ b/todos/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TodosConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'todos' diff --git a/todos/migrations/0001_initial.py b/todos/migrations/0001_initial.py new file mode 100644 index 0000000..520eb09 --- /dev/null +++ b/todos/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2 on 2025-01-15 13:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Todo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('completed', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/todos/migrations/__init__.py b/todos/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/todos/migrations/__pycache__/0001_initial.cpython-312.pyc b/todos/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ece69a4efdb5234a4cd8c11e3c9a16d5ecdb7c67 GIT binary patch literal 1566 zcmZuxzi-<{6h2ZEMN+aOxk?lXsoWH9VhN~a!_d+eg%wM7+r%!AI0hmQ2by=1sqlwN zQcl$=1p>5Vw)#&Q2F<_AKFF?fR}+Xk@y&#-lpIufB<4NpfgQ|2{5a%daN1Kxh5CtF)iNYS+EHZ z_Xwakj&)`yj^6!`X^cRciGHlazY1W9A%cw^M>X1Zi6xS*wk_FFO~WR@a%-p73++SpQMP5lm&)FHO^O&dc))-j1Ys-xjo&uLUz zLy|6k3pH%CodY5&rry;sbwwu<2)E>(<`8M@9?A|iM-dJ40hAFUT#rVf)hC(;q$Nj} z64o)PfeS-QD>D-o4Yf4Y$6oH*S&CTZW2ll3WiHvK4fJ6?7v>-B8;# znyQUpKSW$VkgW)&ZkVQqWh2sN>NJl#s*WR7+AuAwwvC%KojmHuMw=!d?Gqt9Ds45A zSg+k#uhknwGP`)(em{d#8WQ}6DhH;HD@b;FhN_sBQK_3sPsfI1SL%)3ii2$jMqc4z z4_EeOkO-YCguXJ5xfQ5waPbNu&TSxb}SmIRWFlDDLoMk}fdacn{R zVF^WdpMPsyir&Sg$@N)_6K~eX0ScX0C?djVQHg$Ttx`33e(c~i7{wTddBX$d>OWxd YFYv)@{ym0&EcxjLFTL;vP$l&7Kc~QttN;K2 literal 0 HcmV?d00001 diff --git a/todos/migrations/__pycache__/__init__.cpython-312.pyc b/todos/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc708bc6f677f37fab4f044d9b797d9fbecd27fe GIT binary patch literal 193 zcmX@j%ge<81gtyS(?RrO5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!O3=^9&rQ`&Nh~SN z%S_HM%F}nrPcF?(%_}L^cX18VFG(#fiBHK&%uCOYFDp&ePfASAPR&cvFUe2Ik1r_7 z&q_@$0dtD=b2HP65=%1k^NRK3<1_OzOXB183My}L*yQG?l;)(`6|n*>WCY@35aS~= LBO_xGGmr%ULK!r! literal 0 HcmV?d00001 diff --git a/todos/models.py b/todos/models.py new file mode 100644 index 0000000..3b99884 --- /dev/null +++ b/todos/models.py @@ -0,0 +1,12 @@ +from django.db import models +from django.contrib.auth.models import User + +class Todo(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + title = models.CharField(max_length=200) + completed = models.BooleanField(default=False) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.title + diff --git a/todos/serializers.py b/todos/serializers.py new file mode 100644 index 0000000..39546a0 --- /dev/null +++ b/todos/serializers.py @@ -0,0 +1,21 @@ +from rest_framework import serializers +from django.contrib.auth.models import User +from .models import Todo + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('id', 'username') + extra_kwargs = {'password': {'write_only': True}} + + def create(self, validated_data): + user = User.objects.create_user( + username=validated_data['username'], + password=validated_data['password'] + ) + return user + +class TodoSerializer(serializers.ModelSerializer): + class Meta: + model = Todo + fields = ('id', 'title', 'completed', 'created_at') diff --git a/todos/tests.py b/todos/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/todos/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/todos/urls.py b/todos/urls.py new file mode 100644 index 0000000..eba661e --- /dev/null +++ b/todos/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import TodoViewSet, register_user, login_user + +router = DefaultRouter() +router.register(r'todos', TodoViewSet, basename='todo') + +urlpatterns = [ + path('', include(router.urls)), + path('register/', register_user, name='register'), + path('login/', login_user, name='login'), +] diff --git a/todos/views.py b/todos/views.py new file mode 100644 index 0000000..880c1a8 --- /dev/null +++ b/todos/views.py @@ -0,0 +1,51 @@ +from rest_framework import viewsets, permissions, status +from rest_framework.response import Response +from rest_framework.decorators import api_view, permission_classes +from django.contrib.auth import authenticate +from rest_framework_simplejwt.tokens import RefreshToken +from .models import Todo +from .serializers import TodoSerializer, UserSerializer +import logging + +logger = logging.getLogger(__name__) + +@api_view(['POST']) +@permission_classes([permissions.AllowAny]) +def register_user(request): + serializer = UserSerializer(data=request.data) + if serializer.is_valid(): + user = serializer.save() + refresh = RefreshToken.for_user(user) + return Response({ + 'token': str(refresh.access_token), + }) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +@api_view(['POST']) +@permission_classes([permissions.AllowAny]) +def login_user(request): + username = request.data.get('username') + password = request.data.get('password') + user = authenticate(username=username, password=password) + if user: + refresh = RefreshToken.for_user(user) + return Response({ + 'token': str(refresh.access_token), + }) + return Response({'error': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST) + +class TodoViewSet(viewsets.ModelViewSet): + serializer_class = TodoSerializer + permission_classes = [permissions.IsAuthenticated] + + def get_queryset(self): + print(f"user query: {self.request.user.username}") + logger.info(f"query by user: {self.request.user.username}") + return Todo.objects.filter(user=self.request.user) + + def perform_create(self, serializer): + todo = serializer.save(user=self.request.user) + serializer.save(user=self.request.user) + print(f"New todo added - Title: '{todo.title}' by user: {self.request.user.username}") + logger.info(f"New todo added - Title: '{todo.title}' by user: {self.request.user.username}") +