Add /api/notes to get and post notes
This commit is contained in:
@@ -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
12
notes/api_views.py
Normal 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")
|
||||
177
notes/migrations/0001_initial.py
Normal file
177
notes/migrations/0001_initial.py
Normal 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -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
53
notes/serializers.py
Normal 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
9
notes/urls.py
Normal 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")),
|
||||
]
|
||||
Reference in New Issue
Block a user