Add /api/notes to get and post notes

This commit is contained in:
2025-12-07 15:11:49 +05:30
parent af343796d1
commit df595e4f19
10 changed files with 583 additions and 2 deletions

View File

@@ -1,3 +1,28 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from notes.models import Note, User
# Register your models here.
#
@admin.register(User)
class UserAdmin(BaseUserAdmin):
fieldsets = [
*BaseUserAdmin.fieldsets,
("Notes", {"fields": ("allow_notes_from", "expiry_seconds")}),
]
add_fieldsets = [
*BaseUserAdmin.add_fieldsets,
("Notes", {"fields": ("allow_notes_from", "expiry_seconds")}),
]
filter_horizontal = (
*BaseUserAdmin.filter_horizontal,
"allow_notes_from",
)
@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
pass

12
notes/api_views.py Normal file
View File

@@ -0,0 +1,12 @@
from django.utils import timezone
from rest_framework import generics
from notes.models import Note
from notes.serializers import NoteSerializer
class NoteListView(generics.ListCreateAPIView):
serializer_class = NoteSerializer
def get_queryset(self):
return Note.objects.filter(to_user=self.request.user, expiry__gt=timezone.now()).select_related("from_user", "to_user")

View File

@@ -0,0 +1,177 @@
# Generated by Django 5.2.8 on 2025-12-07 08:46
import django.contrib.auth.models
import django.contrib.auth.validators
import django.db.models.deletion
import django.utils.timezone
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="User",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("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",
),
),
(
"username",
models.CharField(
error_messages={
"unique": "A user with that username already exists."
},
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
max_length=150,
unique=True,
validators=[
django.contrib.auth.validators.UnicodeUsernameValidator()
],
verbose_name="username",
),
),
(
"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"
),
),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="email address"
),
),
(
"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",
),
),
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
),
),
("expiry_seconds", models.PositiveIntegerField(default=86400)),
(
"allow_notes_from",
models.ManyToManyField(
blank=True,
related_name="allowed_notes_to",
to=settings.AUTH_USER_MODEL,
),
),
(
"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": "user",
"verbose_name_plural": "users",
"abstract": False,
},
managers=[
("objects", django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name="Note",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("note", models.TextField()),
("expiry", models.DateTimeField()),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"from_user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="created_notes",
to=settings.AUTH_USER_MODEL,
),
),
(
"to_user",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="received_notes",
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View File

@@ -1,3 +1,19 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
# Create your models here.
class User(AbstractUser):
allow_notes_from = models.ManyToManyField('User', related_name='allowed_notes_to', blank=True)
expiry_seconds = models.PositiveIntegerField(default=86400)
class Note(models.Model):
from_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='created_notes')
to_user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='received_notes')
note = models.TextField()
expiry = models.DateTimeField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.from_user.username} -> {self.to_user.username}: {self.note[:30]}"

53
notes/serializers.py Normal file
View File

@@ -0,0 +1,53 @@
from datetime import timedelta
from django.utils import timezone
from rest_framework import serializers
from notes.models import Note, User
class UserSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=150)
class Meta:
model = User
fields = ["id", "username", "first_name", "last_name"]
read_only_fields = ["id", "first_name", "last_name"]
class NoteSerializer(serializers.ModelSerializer):
from_user = UserSerializer(read_only=True)
to_user = UserSerializer()
class Meta:
model = Note
fields = [
"from_user",
"to_user",
"note",
"expiry",
"created_at",
]
read_only_fields = [
"from_user",
"created_at",
"expiry",
]
def validate_to_user(self, value):
try:
user = User.objects.get(username=value['username'])
except User.DoesNotExist:
raise serializers.ValidationError("User not found")
current_user = self.context['request'].user
if not current_user.allowed_notes_to.filter(pk=user.pk).exists():
raise serializers.ValidationError(f"User not allowed to post notes to {user!r}")
return user
def save(self, **kwargs):
to_user = self.validated_data['to_user']
return Note.objects.create(
from_user=self.context['request'].user,
to_user=to_user,
note=self.validated_data['note'],
expiry=timezone.now() + timedelta(seconds=to_user.expiry_seconds)
)

9
notes/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import include, path
from notes import api_views
urlpatterns = [
path("api/notes/", api_views.NoteListView.as_view(), name="api-notes-list"),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
]